blob: 812768b461a471075becd83c7c9481d12191ff51 [file] [log] [blame]
Kaido Kertf309f9a2021-04-30 12:09:15 -07001// 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/api-inl.h"
11#include "src/base/platform/platform.h"
12#include "src/codegen/compilation-cache.h"
13#include "src/execution/arguments.h"
14#include "src/execution/execution.h"
15#include "src/objects/objects-inl.h"
16#include "src/objects/objects.h"
17#include "src/runtime/runtime.h"
18#include "src/strings/unicode-inl.h"
19#include "src/utils/utils.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::V8;
42using ::v8::Value;
43
44
45namespace {
46
47void Returns42(const v8::FunctionCallbackInfo<v8::Value>& info) {
48 info.GetReturnValue().Set(42);
49}
50
51void Return239Callback(Local<String> name,
52 const v8::PropertyCallbackInfo<Value>& info) {
53 ApiTestFuzzer::Fuzz();
54 CheckReturnValue(info, FUNCTION_ADDR(Return239Callback));
55 info.GetReturnValue().Set(v8_str("bad value"));
56 info.GetReturnValue().Set(v8_num(239));
57}
58
59
60void EmptyInterceptorGetter(Local<Name> name,
61 const v8::PropertyCallbackInfo<v8::Value>& info) {}
62
63
64void EmptyInterceptorSetter(Local<Name> name, Local<Value> value,
65 const v8::PropertyCallbackInfo<v8::Value>& info) {}
66
67void EmptyInterceptorQuery(Local<Name> name,
68 const v8::PropertyCallbackInfo<v8::Integer>& info) {}
69
70void EmptyInterceptorDeleter(
71 Local<Name> name, const v8::PropertyCallbackInfo<v8::Boolean>& info) {}
72
73void EmptyInterceptorEnumerator(
74 const v8::PropertyCallbackInfo<v8::Array>& info) {}
75
76void SimpleAccessorGetter(Local<String> name,
77 const v8::PropertyCallbackInfo<v8::Value>& info) {
78 Local<Object> self = Local<Object>::Cast(info.This());
79 info.GetReturnValue().Set(
80 self->Get(info.GetIsolate()->GetCurrentContext(),
81 String::Concat(info.GetIsolate(), v8_str("accessor_"), name))
82 .ToLocalChecked());
83}
84
85void SimpleAccessorSetter(Local<String> name, Local<Value> value,
86 const v8::PropertyCallbackInfo<void>& info) {
87 Local<Object> self = Local<Object>::Cast(info.This());
88 self->Set(info.GetIsolate()->GetCurrentContext(),
89 String::Concat(info.GetIsolate(), v8_str("accessor_"), name), value)
90 .FromJust();
91}
92
93
94void SymbolAccessorGetter(Local<Name> name,
95 const v8::PropertyCallbackInfo<v8::Value>& info) {
96 CHECK(name->IsSymbol());
97 Local<Symbol> sym = Local<Symbol>::Cast(name);
98 if (sym->Description()->IsUndefined()) return;
99 SimpleAccessorGetter(Local<String>::Cast(sym->Description()), info);
100}
101
102void SymbolAccessorSetter(Local<Name> name, Local<Value> value,
103 const v8::PropertyCallbackInfo<void>& info) {
104 CHECK(name->IsSymbol());
105 Local<Symbol> sym = Local<Symbol>::Cast(name);
106 if (sym->Description()->IsUndefined()) return;
107 SimpleAccessorSetter(Local<String>::Cast(sym->Description()), value, info);
108}
109
110void InterceptorGetter(Local<Name> generic_name,
111 const v8::PropertyCallbackInfo<v8::Value>& info) {
112 if (generic_name->IsSymbol()) return;
113 Local<String> name = Local<String>::Cast(generic_name);
114 String::Utf8Value utf8(info.GetIsolate(), name);
115 char* name_str = *utf8;
116 char prefix[] = "interceptor_";
117 int i;
118 for (i = 0; name_str[i] && prefix[i]; ++i) {
119 if (name_str[i] != prefix[i]) return;
120 }
121 Local<Object> self = Local<Object>::Cast(info.This());
122 info.GetReturnValue().Set(
123 self->GetPrivate(
124 info.GetIsolate()->GetCurrentContext(),
125 v8::Private::ForApi(info.GetIsolate(), v8_str(name_str + i)))
126 .ToLocalChecked());
127}
128
129void InterceptorSetter(Local<Name> generic_name, Local<Value> value,
130 const v8::PropertyCallbackInfo<v8::Value>& info) {
131 if (generic_name->IsSymbol()) return;
132 Local<String> name = Local<String>::Cast(generic_name);
133 // Intercept accesses that set certain integer values, for which the name does
134 // not start with 'accessor_'.
135 String::Utf8Value utf8(info.GetIsolate(), name);
136 char* name_str = *utf8;
137 char prefix[] = "accessor_";
138 int i;
139 for (i = 0; name_str[i] && prefix[i]; ++i) {
140 if (name_str[i] != prefix[i]) break;
141 }
142 if (!prefix[i]) return;
143
144 Local<Context> context = info.GetIsolate()->GetCurrentContext();
145 if (value->IsInt32() && value->Int32Value(context).FromJust() < 10000) {
146 Local<Object> self = Local<Object>::Cast(info.This());
147 Local<v8::Private> symbol = v8::Private::ForApi(info.GetIsolate(), name);
148 self->SetPrivate(context, symbol, value).FromJust();
149 info.GetReturnValue().Set(value);
150 }
151}
152
153void GenericInterceptorGetter(Local<Name> generic_name,
154 const v8::PropertyCallbackInfo<v8::Value>& info) {
155 Local<String> str;
156 if (generic_name->IsSymbol()) {
157 Local<Value> name = Local<Symbol>::Cast(generic_name)->Description();
158 if (name->IsUndefined()) return;
159 str = String::Concat(info.GetIsolate(), v8_str("_sym_"),
160 Local<String>::Cast(name));
161 } else {
162 Local<String> name = Local<String>::Cast(generic_name);
163 String::Utf8Value utf8(info.GetIsolate(), name);
164 char* name_str = *utf8;
165 if (*name_str == '_') return;
166 str = String::Concat(info.GetIsolate(), v8_str("_str_"), name);
167 }
168
169 Local<Object> self = Local<Object>::Cast(info.This());
170 info.GetReturnValue().Set(
171 self->Get(info.GetIsolate()->GetCurrentContext(), str).ToLocalChecked());
172}
173
174void GenericInterceptorSetter(Local<Name> generic_name, Local<Value> value,
175 const v8::PropertyCallbackInfo<v8::Value>& info) {
176 Local<String> str;
177 if (generic_name->IsSymbol()) {
178 Local<Value> name = Local<Symbol>::Cast(generic_name)->Description();
179 if (name->IsUndefined()) return;
180 str = String::Concat(info.GetIsolate(), v8_str("_sym_"),
181 Local<String>::Cast(name));
182 } else {
183 Local<String> name = Local<String>::Cast(generic_name);
184 String::Utf8Value utf8(info.GetIsolate(), name);
185 char* name_str = *utf8;
186 if (*name_str == '_') return;
187 str = String::Concat(info.GetIsolate(), v8_str("_str_"), name);
188 }
189
190 Local<Object> self = Local<Object>::Cast(info.This());
191 self->Set(info.GetIsolate()->GetCurrentContext(), str, value).FromJust();
192 info.GetReturnValue().Set(value);
193}
194
195void AddAccessor(Local<FunctionTemplate> templ, Local<String> name,
196 v8::AccessorGetterCallback getter,
197 v8::AccessorSetterCallback setter) {
198 templ->PrototypeTemplate()->SetAccessor(name, getter, setter);
199}
200
201void AddAccessor(Local<FunctionTemplate> templ, Local<Name> name,
202 v8::AccessorNameGetterCallback getter,
203 v8::AccessorNameSetterCallback setter) {
204 templ->PrototypeTemplate()->SetAccessor(name, getter, setter);
205}
206
207void AddStringOnlyInterceptor(Local<FunctionTemplate> templ,
208 v8::GenericNamedPropertyGetterCallback getter,
209 v8::GenericNamedPropertySetterCallback setter) {
210 templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
211 getter, setter, nullptr, nullptr, nullptr, Local<v8::Value>(),
212 v8::PropertyHandlerFlags::kOnlyInterceptStrings));
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 CheckThisIndexedPropertyDefiner(
244 uint32_t index, const v8::PropertyDescriptor& desc,
245 const v8::PropertyCallbackInfo<v8::Value>& info) {
246 CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyDefiner));
247 ApiTestFuzzer::Fuzz();
248 CHECK(info.This()
249 ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
250 .FromJust());
251}
252
253void CheckThisNamedPropertyDefiner(
254 Local<Name> property, const v8::PropertyDescriptor& desc,
255 const v8::PropertyCallbackInfo<v8::Value>& info) {
256 CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyDefiner));
257 ApiTestFuzzer::Fuzz();
258 CHECK(info.This()
259 ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
260 .FromJust());
261}
262
263void CheckThisIndexedPropertySetter(
264 uint32_t index, Local<Value> value,
265 const v8::PropertyCallbackInfo<v8::Value>& info) {
266 CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertySetter));
267 ApiTestFuzzer::Fuzz();
268 CHECK(info.This()
269 ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
270 .FromJust());
271}
272
273void CheckThisIndexedPropertyDescriptor(
274 uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
275 CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyDescriptor));
276 ApiTestFuzzer::Fuzz();
277 CHECK(info.This()
278 ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
279 .FromJust());
280}
281
282void CheckThisNamedPropertyDescriptor(
283 Local<Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
284 CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyDescriptor));
285 ApiTestFuzzer::Fuzz();
286 CHECK(info.This()
287 ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
288 .FromJust());
289}
290
291void CheckThisNamedPropertySetter(
292 Local<Name> property, Local<Value> value,
293 const v8::PropertyCallbackInfo<v8::Value>& info) {
294 CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertySetter));
295 ApiTestFuzzer::Fuzz();
296 CHECK(info.This()
297 ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
298 .FromJust());
299}
300
301void CheckThisIndexedPropertyQuery(
302 uint32_t index, const v8::PropertyCallbackInfo<v8::Integer>& info) {
303 CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyQuery));
304 ApiTestFuzzer::Fuzz();
305 CHECK(info.This()
306 ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
307 .FromJust());
308}
309
310
311void CheckThisNamedPropertyQuery(
312 Local<Name> property, const v8::PropertyCallbackInfo<v8::Integer>& info) {
313 CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyQuery));
314 ApiTestFuzzer::Fuzz();
315 CHECK(info.This()
316 ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
317 .FromJust());
318}
319
320
321void CheckThisIndexedPropertyDeleter(
322 uint32_t index, const v8::PropertyCallbackInfo<v8::Boolean>& info) {
323 CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyDeleter));
324 ApiTestFuzzer::Fuzz();
325 CHECK(info.This()
326 ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
327 .FromJust());
328}
329
330
331void CheckThisNamedPropertyDeleter(
332 Local<Name> property, const v8::PropertyCallbackInfo<v8::Boolean>& info) {
333 CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyDeleter));
334 ApiTestFuzzer::Fuzz();
335 CHECK(info.This()
336 ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
337 .FromJust());
338}
339
340
341void CheckThisIndexedPropertyEnumerator(
342 const v8::PropertyCallbackInfo<v8::Array>& info) {
343 CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyEnumerator));
344 ApiTestFuzzer::Fuzz();
345 CHECK(info.This()
346 ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
347 .FromJust());
348}
349
350
351void CheckThisNamedPropertyEnumerator(
352 const v8::PropertyCallbackInfo<v8::Array>& info) {
353 CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyEnumerator));
354 ApiTestFuzzer::Fuzz();
355 CHECK(info.This()
356 ->Equals(info.GetIsolate()->GetCurrentContext(), bottom)
357 .FromJust());
358}
359
360
361int echo_named_call_count;
362
363
364void EchoNamedProperty(Local<Name> name,
365 const v8::PropertyCallbackInfo<v8::Value>& info) {
366 ApiTestFuzzer::Fuzz();
367 CHECK(v8_str("data")
368 ->Equals(info.GetIsolate()->GetCurrentContext(), info.Data())
369 .FromJust());
370 echo_named_call_count++;
371 info.GetReturnValue().Set(name);
372}
373
374void InterceptorHasOwnPropertyGetter(
375 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
376 ApiTestFuzzer::Fuzz();
377}
378
379void InterceptorHasOwnPropertyGetterGC(
380 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
381 ApiTestFuzzer::Fuzz();
382 CcTest::CollectAllGarbage();
383}
384
385int query_counter_int = 0;
386
387void QueryCallback(Local<Name> property,
388 const v8::PropertyCallbackInfo<v8::Integer>& info) {
389 query_counter_int++;
390}
391
392} // namespace
393
394// Examples that show when the query callback is triggered.
395THREADED_TEST(QueryInterceptor) {
396 v8::Isolate* isolate = CcTest::isolate();
397 v8::HandleScope scope(isolate);
398 v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
399 templ->InstanceTemplate()->SetHandler(
400 v8::NamedPropertyHandlerConfiguration(nullptr, nullptr, QueryCallback));
401 LocalContext env;
402 env->Global()
403 ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
404 .ToLocalChecked()
405 ->NewInstance(env.local())
406 .ToLocalChecked())
407 .FromJust();
408 CHECK_EQ(0, query_counter_int);
409 v8::Local<Value> result =
410 v8_compile("Object.getOwnPropertyDescriptor(obj, 'x');")
411 ->Run(env.local())
412 .ToLocalChecked();
413 CHECK_EQ(1, query_counter_int);
414 CHECK_EQ(v8::PropertyAttribute::None,
415 static_cast<v8::PropertyAttribute>(
416 result->Int32Value(env.local()).FromJust()));
417
418 v8_compile("Object.defineProperty(obj, 'not_enum', {value: 17});")
419 ->Run(env.local())
420 .ToLocalChecked();
421 CHECK_EQ(2, query_counter_int);
422
423 v8_compile(
424 "Object.defineProperty(obj, 'enum', {value: 17, enumerable: true, "
425 "writable: true});")
426 ->Run(env.local())
427 .ToLocalChecked();
428 CHECK_EQ(3, query_counter_int);
429
430 CHECK(v8_compile("obj.propertyIsEnumerable('enum');")
431 ->Run(env.local())
432 .ToLocalChecked()
433 ->BooleanValue(isolate));
434 CHECK_EQ(4, query_counter_int);
435
436 CHECK(!v8_compile("obj.propertyIsEnumerable('not_enum');")
437 ->Run(env.local())
438 .ToLocalChecked()
439 ->BooleanValue(isolate));
440 CHECK_EQ(5, query_counter_int);
441
442 CHECK(v8_compile("obj.hasOwnProperty('enum');")
443 ->Run(env.local())
444 .ToLocalChecked()
445 ->BooleanValue(isolate));
446 CHECK_EQ(5, query_counter_int);
447
448 CHECK(v8_compile("obj.hasOwnProperty('not_enum');")
449 ->Run(env.local())
450 .ToLocalChecked()
451 ->BooleanValue(isolate));
452 CHECK_EQ(5, query_counter_int);
453
454 CHECK(!v8_compile("obj.hasOwnProperty('x');")
455 ->Run(env.local())
456 .ToLocalChecked()
457 ->BooleanValue(isolate));
458 CHECK_EQ(6, query_counter_int);
459
460 CHECK(!v8_compile("obj.propertyIsEnumerable('undef');")
461 ->Run(env.local())
462 .ToLocalChecked()
463 ->BooleanValue(isolate));
464 CHECK_EQ(7, query_counter_int);
465
466 v8_compile("Object.defineProperty(obj, 'enum', {value: 42});")
467 ->Run(env.local())
468 .ToLocalChecked();
469 CHECK_EQ(8, query_counter_int);
470
471 v8_compile("Object.isFrozen('obj.x');")->Run(env.local()).ToLocalChecked();
472 CHECK_EQ(8, query_counter_int);
473
474 v8_compile("'x' in obj;")->Run(env.local()).ToLocalChecked();
475 CHECK_EQ(9, query_counter_int);
476}
477
478namespace {
479
480bool get_was_called = false;
481bool set_was_called = false;
482
483int set_was_called_counter = 0;
484
485void GetterCallback(Local<Name> property,
486 const v8::PropertyCallbackInfo<v8::Value>& info) {
487 get_was_called = true;
488}
489
490void SetterCallback(Local<Name> property, Local<Value> value,
491 const v8::PropertyCallbackInfo<v8::Value>& info) {
492 set_was_called = true;
493 set_was_called_counter++;
494}
495
496void InterceptingSetterCallback(
497 Local<Name> property, Local<Value> value,
498 const v8::PropertyCallbackInfo<v8::Value>& info) {
499 info.GetReturnValue().Set(value);
500}
501
502} // namespace
503
504// Check that get callback is called in defineProperty with accessor descriptor.
505THREADED_TEST(DefinerCallbackAccessorInterceptor) {
506 v8::HandleScope scope(CcTest::isolate());
507 v8::Local<v8::FunctionTemplate> templ =
508 v8::FunctionTemplate::New(CcTest::isolate());
509 templ->InstanceTemplate()->SetHandler(
510 v8::NamedPropertyHandlerConfiguration(GetterCallback, SetterCallback));
511 LocalContext env;
512 env->Global()
513 ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
514 .ToLocalChecked()
515 ->NewInstance(env.local())
516 .ToLocalChecked())
517 .FromJust();
518
519 get_was_called = false;
520 set_was_called = false;
521
522 v8_compile("Object.defineProperty(obj, 'x', {set: function() {return 17;}});")
523 ->Run(env.local())
524 .ToLocalChecked();
525 CHECK(get_was_called);
526 CHECK(!set_was_called);
527}
528
529// Check that set callback is called for function declarations.
530THREADED_TEST(SetterCallbackFunctionDeclarationInterceptor) {
531 v8::HandleScope scope(CcTest::isolate());
532 LocalContext env;
533 v8::Local<v8::FunctionTemplate> templ =
534 v8::FunctionTemplate::New(CcTest::isolate());
535
536 v8::Local<ObjectTemplate> object_template = templ->InstanceTemplate();
537 object_template->SetHandler(
538 v8::NamedPropertyHandlerConfiguration(nullptr, SetterCallback));
539 v8::Local<v8::Context> ctx =
540 v8::Context::New(CcTest::isolate(), nullptr, object_template);
541
542 set_was_called_counter = 0;
543
544 // Declare function.
545 v8::Local<v8::String> code = v8_str("function x() {return 42;}; x();");
546 CHECK_EQ(42, v8::Script::Compile(ctx, code)
547 .ToLocalChecked()
548 ->Run(ctx)
549 .ToLocalChecked()
550 ->Int32Value(ctx)
551 .FromJust());
552 CHECK_EQ(1, set_was_called_counter);
553
554 // Redeclare function.
555 code = v8_str("function x() {return 43;}; x();");
556 CHECK_EQ(43, v8::Script::Compile(ctx, code)
557 .ToLocalChecked()
558 ->Run(ctx)
559 .ToLocalChecked()
560 ->Int32Value(ctx)
561 .FromJust());
562 CHECK_EQ(2, set_was_called_counter);
563
564 // Redefine function.
565 code = v8_str("x = function() {return 44;}; x();");
566 CHECK_EQ(44, v8::Script::Compile(ctx, code)
567 .ToLocalChecked()
568 ->Run(ctx)
569 .ToLocalChecked()
570 ->Int32Value(ctx)
571 .FromJust());
572 CHECK_EQ(3, set_was_called_counter);
573}
574
575namespace {
576int descriptor_was_called;
577
578void PropertyDescriptorCallback(
579 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
580 // Intercept the callback by setting a different descriptor.
581 descriptor_was_called++;
582 const char* code =
583 "var desc = {value: 5};"
584 "desc;";
585 Local<Value> descriptor = v8_compile(code)
586 ->Run(info.GetIsolate()->GetCurrentContext())
587 .ToLocalChecked();
588 info.GetReturnValue().Set(descriptor);
589}
590} // namespace
591
592// Check that the descriptor callback is called on the global object.
593THREADED_TEST(DescriptorCallbackOnGlobalObject) {
594 v8::HandleScope scope(CcTest::isolate());
595 LocalContext env;
596 v8::Local<v8::FunctionTemplate> templ =
597 v8::FunctionTemplate::New(CcTest::isolate());
598
599 v8::Local<ObjectTemplate> object_template = templ->InstanceTemplate();
600 object_template->SetHandler(v8::NamedPropertyHandlerConfiguration(
601 nullptr, nullptr, PropertyDescriptorCallback, nullptr, nullptr, nullptr));
602 v8::Local<v8::Context> ctx =
603 v8::Context::New(CcTest::isolate(), nullptr, object_template);
604
605 descriptor_was_called = 0;
606
607 // Declare function.
608 v8::Local<v8::String> code = v8_str(
609 "var x = 42; var desc = Object.getOwnPropertyDescriptor(this, 'x'); "
610 "desc.value;");
611 CHECK_EQ(5, v8::Script::Compile(ctx, code)
612 .ToLocalChecked()
613 ->Run(ctx)
614 .ToLocalChecked()
615 ->Int32Value(ctx)
616 .FromJust());
617 CHECK_EQ(1, descriptor_was_called);
618}
619
620namespace {
621void QueryCallbackSetDontDelete(
622 Local<Name> property, const v8::PropertyCallbackInfo<v8::Integer>& info) {
623 info.GetReturnValue().Set(v8::PropertyAttribute::DontDelete);
624}
625
626} // namespace
627
628// Regression for a Node.js test that fails in debug mode.
629THREADED_TEST(InterceptorFunctionRedeclareWithQueryCallback) {
630 v8::HandleScope scope(CcTest::isolate());
631 LocalContext env;
632 v8::Local<v8::FunctionTemplate> templ =
633 v8::FunctionTemplate::New(CcTest::isolate());
634
635 v8::Local<ObjectTemplate> object_template = templ->InstanceTemplate();
636 object_template->SetHandler(v8::NamedPropertyHandlerConfiguration(
637 nullptr, nullptr, QueryCallbackSetDontDelete));
638 v8::Local<v8::Context> ctx =
639 v8::Context::New(CcTest::isolate(), nullptr, object_template);
640
641 // Declare and redeclare function.
642 v8::Local<v8::String> code = v8_str(
643 "function x() {return 42;};"
644 "function x() {return 43;};");
645 v8::Script::Compile(ctx, code).ToLocalChecked()->Run(ctx).ToLocalChecked();
646}
647
648// Regression test for chromium bug 656648.
649// Do not crash on non-masking, intercepting setter callbacks.
650THREADED_TEST(NonMaskingInterceptor) {
651 v8::HandleScope scope(CcTest::isolate());
652 LocalContext env;
653 v8::Local<v8::FunctionTemplate> templ =
654 v8::FunctionTemplate::New(CcTest::isolate());
655
656 v8::Local<ObjectTemplate> object_template = templ->InstanceTemplate();
657 object_template->SetHandler(v8::NamedPropertyHandlerConfiguration(
658 nullptr, InterceptingSetterCallback, nullptr, nullptr, nullptr,
659 Local<Value>(), v8::PropertyHandlerFlags::kNonMasking));
660 v8::Local<v8::Context> ctx =
661 v8::Context::New(CcTest::isolate(), nullptr, object_template);
662
663 v8::Local<v8::String> code = v8_str("function x() {return 43;};");
664 v8::Script::Compile(ctx, code).ToLocalChecked()->Run(ctx).ToLocalChecked();
665}
666
667// Check that function re-declarations throw if they are read-only.
668THREADED_TEST(SetterCallbackFunctionDeclarationInterceptorThrow) {
669 v8::HandleScope scope(CcTest::isolate());
670 LocalContext env;
671 v8::Local<v8::FunctionTemplate> templ =
672 v8::FunctionTemplate::New(CcTest::isolate());
673
674 v8::Local<ObjectTemplate> object_template = templ->InstanceTemplate();
675 object_template->SetHandler(
676 v8::NamedPropertyHandlerConfiguration(nullptr, SetterCallback));
677 v8::Local<v8::Context> ctx =
678 v8::Context::New(CcTest::isolate(), nullptr, object_template);
679
680 set_was_called = false;
681
682 v8::Local<v8::String> code = v8_str(
683 "function x() {return 42;};"
684 "Object.defineProperty(this, 'x', {"
685 "configurable: false, "
686 "writable: false});"
687 "x();");
688 CHECK_EQ(42, v8::Script::Compile(ctx, code)
689 .ToLocalChecked()
690 ->Run(ctx)
691 .ToLocalChecked()
692 ->Int32Value(ctx)
693 .FromJust());
694
695 CHECK(set_was_called);
696
697 v8::TryCatch try_catch(CcTest::isolate());
698 set_was_called = false;
699
700 // Redeclare function that is read-only.
701 code = v8_str("function x() {return 43;};");
702 CHECK(v8::Script::Compile(ctx, code).ToLocalChecked()->Run(ctx).IsEmpty());
703 CHECK(try_catch.HasCaught());
704
705 CHECK(!set_was_called);
706}
707
708
709namespace {
710
711bool get_was_called_in_order = false;
712bool define_was_called_in_order = false;
713
714void GetterCallbackOrder(Local<Name> property,
715 const v8::PropertyCallbackInfo<v8::Value>& info) {
716 get_was_called_in_order = true;
717 CHECK(!define_was_called_in_order);
718 info.GetReturnValue().Set(property);
719}
720
721void DefinerCallbackOrder(Local<Name> property,
722 const v8::PropertyDescriptor& desc,
723 const v8::PropertyCallbackInfo<v8::Value>& info) {
724 // Get called before DefineProperty because we query the descriptor first.
725 CHECK(get_was_called_in_order);
726 define_was_called_in_order = true;
727}
728
729} // namespace
730
731// Check that getter callback is called before definer callback.
732THREADED_TEST(DefinerCallbackGetAndDefine) {
733 v8::HandleScope scope(CcTest::isolate());
734 v8::Local<v8::FunctionTemplate> templ =
735 v8::FunctionTemplate::New(CcTest::isolate());
736 templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
737 GetterCallbackOrder, SetterCallback, nullptr, nullptr, nullptr,
738 DefinerCallbackOrder));
739 LocalContext env;
740 env->Global()
741 ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
742 .ToLocalChecked()
743 ->NewInstance(env.local())
744 .ToLocalChecked())
745 .FromJust();
746
747 CHECK(!get_was_called_in_order);
748 CHECK(!define_was_called_in_order);
749
750 v8_compile("Object.defineProperty(obj, 'x', {set: function() {return 17;}});")
751 ->Run(env.local())
752 .ToLocalChecked();
753 CHECK(get_was_called_in_order);
754 CHECK(define_was_called_in_order);
755}
756
757namespace { // namespace for InObjectLiteralDefinitionWithInterceptor
758
759// Workaround for no-snapshot builds: only intercept once Context::New() is
760// done, otherwise we'll intercept
761// bootstrapping like defining array on the global object.
762bool context_is_done = false;
763bool getter_callback_was_called = false;
764
765void ReturnUndefinedGetterCallback(
766 Local<Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
767 if (context_is_done) {
768 getter_callback_was_called = true;
769 info.GetReturnValue().SetUndefined();
770 }
771}
772
773} // namespace
774
775// Check that an interceptor is not invoked during ES6 style definitions inside
776// an object literal.
777THREADED_TEST(InObjectLiteralDefinitionWithInterceptor) {
778 v8::HandleScope scope(CcTest::isolate());
779 LocalContext env;
780
781 // Set up a context in which all global object definitions are intercepted.
782 v8::Local<v8::FunctionTemplate> templ =
783 v8::FunctionTemplate::New(CcTest::isolate());
784 v8::Local<ObjectTemplate> object_template = templ->InstanceTemplate();
785 object_template->SetHandler(
786 v8::NamedPropertyHandlerConfiguration(ReturnUndefinedGetterCallback));
787 v8::Local<v8::Context> ctx =
788 v8::Context::New(CcTest::isolate(), nullptr, object_template);
789
790 context_is_done = true;
791
792 // The interceptor returns undefined for any global object,
793 // so setting a property on an object should throw.
794 v8::Local<v8::String> code = v8_str("var o = {}; o.x = 5");
795 {
796 getter_callback_was_called = false;
797 v8::TryCatch try_catch(CcTest::isolate());
798 CHECK(v8::Script::Compile(ctx, code).ToLocalChecked()->Run(ctx).IsEmpty());
799 CHECK(try_catch.HasCaught());
800 CHECK(getter_callback_was_called);
801 }
802
803 // Defining a property in the object literal should not throw
804 // because the interceptor is not invoked.
805 {
806 getter_callback_was_called = false;
807 v8::TryCatch try_catch(CcTest::isolate());
808 code = v8_str("var l = {x: 5};");
809 CHECK(v8::Script::Compile(ctx, code)
810 .ToLocalChecked()
811 ->Run(ctx)
812 .ToLocalChecked()
813 ->IsUndefined());
814 CHECK(!try_catch.HasCaught());
815 CHECK(!getter_callback_was_called);
816 }
817}
818
819THREADED_TEST(InterceptorHasOwnProperty) {
820 LocalContext context;
821 v8::Isolate* isolate = context->GetIsolate();
822 v8::HandleScope scope(isolate);
823 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(isolate);
824 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
825 instance_templ->SetHandler(
826 v8::NamedPropertyHandlerConfiguration(InterceptorHasOwnPropertyGetter));
827 Local<Function> function =
828 fun_templ->GetFunction(context.local()).ToLocalChecked();
829 context->Global()
830 ->Set(context.local(), v8_str("constructor"), function)
831 .FromJust();
832 v8::Local<Value> value = CompileRun(
833 "var o = new constructor();"
834 "o.hasOwnProperty('ostehaps');");
835 CHECK(!value->BooleanValue(isolate));
836 value = CompileRun(
837 "o.ostehaps = 42;"
838 "o.hasOwnProperty('ostehaps');");
839 CHECK(value->BooleanValue(isolate));
840 value = CompileRun(
841 "var p = new constructor();"
842 "p.hasOwnProperty('ostehaps');");
843 CHECK(!value->BooleanValue(isolate));
844}
845
846
847THREADED_TEST(InterceptorHasOwnPropertyCausingGC) {
848 LocalContext context;
849 v8::Isolate* isolate = context->GetIsolate();
850 v8::HandleScope scope(isolate);
851 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(isolate);
852 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
853 instance_templ->SetHandler(
854 v8::NamedPropertyHandlerConfiguration(InterceptorHasOwnPropertyGetterGC));
855 Local<Function> function =
856 fun_templ->GetFunction(context.local()).ToLocalChecked();
857 context->Global()
858 ->Set(context.local(), v8_str("constructor"), function)
859 .FromJust();
860 // Let's first make some stuff so we can be sure to get a good GC.
861 CompileRun(
862 "function makestr(size) {"
863 " switch (size) {"
864 " case 1: return 'f';"
865 " case 2: return 'fo';"
866 " case 3: return 'foo';"
867 " }"
868 " return makestr(size >> 1) + makestr((size + 1) >> 1);"
869 "}"
870 "var x = makestr(12345);"
871 "x = makestr(31415);"
872 "x = makestr(23456);");
873 v8::Local<Value> value = CompileRun(
874 "var o = new constructor();"
875 "o.__proto__ = new String(x);"
876 "o.hasOwnProperty('ostehaps');");
877 CHECK(!value->BooleanValue(isolate));
878}
879
880static void CheckInterceptorIC(v8::GenericNamedPropertyGetterCallback getter,
881 v8::GenericNamedPropertyQueryCallback query,
882 const char* source, int expected) {
883 v8::Isolate* isolate = CcTest::isolate();
884 v8::HandleScope scope(isolate);
885 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
886 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
887 getter, nullptr, query, nullptr, nullptr, v8_str("data")));
888 LocalContext context;
889 context->Global()
890 ->Set(context.local(), v8_str("o"),
891 templ->NewInstance(context.local()).ToLocalChecked())
892 .FromJust();
893 v8::Local<Value> value = CompileRun(source);
894 CHECK_EQ(expected, value->Int32Value(context.local()).FromJust());
895}
896
897static void CheckInterceptorLoadIC(
898 v8::GenericNamedPropertyGetterCallback getter, const char* source,
899 int expected) {
900 CheckInterceptorIC(getter, nullptr, source, expected);
901}
902
903static void InterceptorLoadICGetter(
904 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
905 ApiTestFuzzer::Fuzz();
906 v8::Isolate* isolate = CcTest::isolate();
907 CHECK_EQ(isolate, info.GetIsolate());
908 v8::Local<v8::Context> context = isolate->GetCurrentContext();
909 CHECK(v8_str("data")->Equals(context, info.Data()).FromJust());
910 CHECK(v8_str("x")->Equals(context, name).FromJust());
911 info.GetReturnValue().Set(v8::Integer::New(isolate, 42));
912}
913
914
915// This test should hit the load IC for the interceptor case.
916THREADED_TEST(InterceptorLoadIC) {
917 CheckInterceptorLoadIC(InterceptorLoadICGetter,
918 "var result = 0;"
919 "for (var i = 0; i < 1000; i++) {"
920 " result = o.x;"
921 "}",
922 42);
923}
924
925
926// Below go several tests which verify that JITing for various
927// configurations of interceptor and explicit fields works fine
928// (those cases are special cased to get better performance).
929
930static void InterceptorLoadXICGetter(
931 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
932 ApiTestFuzzer::Fuzz();
933 info.GetReturnValue().Set(
934 v8_str("x")
935 ->Equals(info.GetIsolate()->GetCurrentContext(), name)
936 .FromJust()
937 ? v8::Local<v8::Value>(v8::Integer::New(info.GetIsolate(), 42))
938 : v8::Local<v8::Value>());
939}
940
941
942THREADED_TEST(InterceptorLoadICWithFieldOnHolder) {
943 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
944 "var result = 0;"
945 "o.y = 239;"
946 "for (var i = 0; i < 1000; i++) {"
947 " result = o.y;"
948 "}",
949 239);
950}
951
952
953THREADED_TEST(InterceptorLoadICWithSubstitutedProto) {
954 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
955 "var result = 0;"
956 "o.__proto__ = { 'y': 239 };"
957 "for (var i = 0; i < 1000; i++) {"
958 " result = o.y + o.x;"
959 "}",
960 239 + 42);
961}
962
963
964THREADED_TEST(InterceptorLoadICWithPropertyOnProto) {
965 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
966 "var result = 0;"
967 "o.__proto__.y = 239;"
968 "for (var i = 0; i < 1000; i++) {"
969 " result = o.y + o.x;"
970 "}",
971 239 + 42);
972}
973
974
975THREADED_TEST(InterceptorLoadICUndefined) {
976 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
977 "var result = 0;"
978 "for (var i = 0; i < 1000; i++) {"
979 " result = (o.y == undefined) ? 239 : 42;"
980 "}",
981 239);
982}
983
984
985THREADED_TEST(InterceptorLoadICWithOverride) {
986 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
987 "fst = new Object(); fst.__proto__ = o;"
988 "snd = new Object(); snd.__proto__ = fst;"
989 "var result1 = 0;"
990 "for (var i = 0; i < 1000; i++) {"
991 " result1 = snd.x;"
992 "}"
993 "fst.x = 239;"
994 "var result = 0;"
995 "for (var i = 0; i < 1000; i++) {"
996 " result = snd.x;"
997 "}"
998 "result + result1",
999 239 + 42);
1000}
1001
1002
1003// Test the case when we stored field into
1004// a stub, but interceptor produced value on its own.
1005THREADED_TEST(InterceptorLoadICFieldNotNeeded) {
1006 CheckInterceptorLoadIC(
1007 InterceptorLoadXICGetter,
1008 "proto = new Object();"
1009 "o.__proto__ = proto;"
1010 "proto.x = 239;"
1011 "for (var i = 0; i < 1000; i++) {"
1012 " o.x;"
1013 // Now it should be ICed and keep a reference to x defined on proto
1014 "}"
1015 "var result = 0;"
1016 "for (var i = 0; i < 1000; i++) {"
1017 " result += o.x;"
1018 "}"
1019 "result;",
1020 42 * 1000);
1021}
1022
1023
1024// Test the case when we stored field into
1025// a stub, but it got invalidated later on.
1026THREADED_TEST(InterceptorLoadICInvalidatedField) {
1027 CheckInterceptorLoadIC(
1028 InterceptorLoadXICGetter,
1029 "proto1 = new Object();"
1030 "proto2 = new Object();"
1031 "o.__proto__ = proto1;"
1032 "proto1.__proto__ = proto2;"
1033 "proto2.y = 239;"
1034 "for (var i = 0; i < 1000; i++) {"
1035 " o.y;"
1036 // Now it should be ICed and keep a reference to y defined on proto2
1037 "}"
1038 "proto1.y = 42;"
1039 "var result = 0;"
1040 "for (var i = 0; i < 1000; i++) {"
1041 " result += o.y;"
1042 "}"
1043 "result;",
1044 42 * 1000);
1045}
1046
1047
1048static int interceptor_load_not_handled_calls = 0;
1049static void InterceptorLoadNotHandled(
1050 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
1051 ++interceptor_load_not_handled_calls;
1052}
1053
1054
1055// Test how post-interceptor lookups are done in the non-cacheable
1056// case: the interceptor should not be invoked during this lookup.
1057THREADED_TEST(InterceptorLoadICPostInterceptor) {
1058 interceptor_load_not_handled_calls = 0;
1059 CheckInterceptorLoadIC(InterceptorLoadNotHandled,
1060 "receiver = new Object();"
1061 "receiver.__proto__ = o;"
1062 "proto = new Object();"
1063 "/* Make proto a slow-case object. */"
1064 "for (var i = 0; i < 1000; i++) {"
1065 " proto[\"xxxxxxxx\" + i] = [];"
1066 "}"
1067 "proto.x = 17;"
1068 "o.__proto__ = proto;"
1069 "var result = 0;"
1070 "for (var i = 0; i < 1000; i++) {"
1071 " result += receiver.x;"
1072 "}"
1073 "result;",
1074 17 * 1000);
1075 CHECK_EQ(1000, interceptor_load_not_handled_calls);
1076}
1077
1078
1079// Test the case when we stored field into
1080// a stub, but it got invalidated later on due to override on
1081// global object which is between interceptor and fields' holders.
1082THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) {
1083 CheckInterceptorLoadIC(
1084 InterceptorLoadXICGetter,
1085 "o.__proto__ = this;" // set a global to be a proto of o.
1086 "this.__proto__.y = 239;"
1087 "for (var i = 0; i < 10; i++) {"
1088 " if (o.y != 239) throw 'oops: ' + o.y;"
1089 // Now it should be ICed and keep a reference to y defined on
1090 // field_holder.
1091 "}"
1092 "this.y = 42;" // Assign on a global.
1093 "var result = 0;"
1094 "for (var i = 0; i < 10; i++) {"
1095 " result += o.y;"
1096 "}"
1097 "result;",
1098 42 * 10);
1099}
1100
1101
1102static void SetOnThis(Local<String> name, Local<Value> value,
1103 const v8::PropertyCallbackInfo<void>& info) {
1104 Local<Object>::Cast(info.This())
1105 ->CreateDataProperty(info.GetIsolate()->GetCurrentContext(), name, value)
1106 .FromJust();
1107}
1108
1109
1110THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) {
1111 v8::Isolate* isolate = CcTest::isolate();
1112 v8::HandleScope scope(isolate);
1113 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
1114 templ->SetHandler(
1115 v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
1116 templ->SetAccessor(v8_str("y"), Return239Callback);
1117 LocalContext context;
1118 context->Global()
1119 ->Set(context.local(), v8_str("o"),
1120 templ->NewInstance(context.local()).ToLocalChecked())
1121 .FromJust();
1122
1123 // Check the case when receiver and interceptor's holder
1124 // are the same objects.
1125 v8::Local<Value> value = CompileRun(
1126 "var result = 0;"
1127 "for (var i = 0; i < 7; i++) {"
1128 " result = o.y;"
1129 "}");
1130 CHECK_EQ(239, value->Int32Value(context.local()).FromJust());
1131
1132 // Check the case when interceptor's holder is in proto chain
1133 // of receiver.
1134 value = CompileRun(
1135 "r = { __proto__: o };"
1136 "var result = 0;"
1137 "for (var i = 0; i < 7; i++) {"
1138 " result = r.y;"
1139 "}");
1140 CHECK_EQ(239, value->Int32Value(context.local()).FromJust());
1141}
1142
1143
1144THREADED_TEST(InterceptorLoadICWithCallbackOnProto) {
1145 v8::Isolate* isolate = CcTest::isolate();
1146 v8::HandleScope scope(isolate);
1147 v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
1148 templ_o->SetHandler(
1149 v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
1150 v8::Local<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate);
1151 templ_p->SetAccessor(v8_str("y"), Return239Callback);
1152
1153 LocalContext context;
1154 context->Global()
1155 ->Set(context.local(), v8_str("o"),
1156 templ_o->NewInstance(context.local()).ToLocalChecked())
1157 .FromJust();
1158 context->Global()
1159 ->Set(context.local(), v8_str("p"),
1160 templ_p->NewInstance(context.local()).ToLocalChecked())
1161 .FromJust();
1162
1163 // Check the case when receiver and interceptor's holder
1164 // are the same objects.
1165 v8::Local<Value> value = CompileRun(
1166 "o.__proto__ = p;"
1167 "var result = 0;"
1168 "for (var i = 0; i < 7; i++) {"
1169 " result = o.x + o.y;"
1170 "}");
1171 CHECK_EQ(239 + 42, value->Int32Value(context.local()).FromJust());
1172
1173 // Check the case when interceptor's holder is in proto chain
1174 // of receiver.
1175 value = CompileRun(
1176 "r = { __proto__: o };"
1177 "var result = 0;"
1178 "for (var i = 0; i < 7; i++) {"
1179 " result = r.x + r.y;"
1180 "}");
1181 CHECK_EQ(239 + 42, value->Int32Value(context.local()).FromJust());
1182}
1183
1184
1185THREADED_TEST(InterceptorLoadICForCallbackWithOverride) {
1186 v8::Isolate* isolate = CcTest::isolate();
1187 v8::HandleScope scope(isolate);
1188 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
1189 templ->SetHandler(
1190 v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
1191 templ->SetAccessor(v8_str("y"), Return239Callback);
1192
1193 LocalContext context;
1194 context->Global()
1195 ->Set(context.local(), v8_str("o"),
1196 templ->NewInstance(context.local()).ToLocalChecked())
1197 .FromJust();
1198
1199 v8::Local<Value> value = CompileRun(
1200 "fst = new Object(); fst.__proto__ = o;"
1201 "snd = new Object(); snd.__proto__ = fst;"
1202 "var result1 = 0;"
1203 "for (var i = 0; i < 7; i++) {"
1204 " result1 = snd.x;"
1205 "}"
1206 "fst.x = 239;"
1207 "var result = 0;"
1208 "for (var i = 0; i < 7; i++) {"
1209 " result = snd.x;"
1210 "}"
1211 "result + result1");
1212 CHECK_EQ(239 + 42, value->Int32Value(context.local()).FromJust());
1213}
1214
1215
1216// Test the case when we stored callback into
1217// a stub, but interceptor produced value on its own.
1218THREADED_TEST(InterceptorLoadICCallbackNotNeeded) {
1219 v8::Isolate* isolate = CcTest::isolate();
1220 v8::HandleScope scope(isolate);
1221 v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
1222 templ_o->SetHandler(
1223 v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
1224 v8::Local<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate);
1225 templ_p->SetAccessor(v8_str("y"), Return239Callback);
1226
1227 LocalContext context;
1228 context->Global()
1229 ->Set(context.local(), v8_str("o"),
1230 templ_o->NewInstance(context.local()).ToLocalChecked())
1231 .FromJust();
1232 context->Global()
1233 ->Set(context.local(), v8_str("p"),
1234 templ_p->NewInstance(context.local()).ToLocalChecked())
1235 .FromJust();
1236
1237 v8::Local<Value> value = CompileRun(
1238 "o.__proto__ = p;"
1239 "for (var i = 0; i < 7; i++) {"
1240 " o.x;"
1241 // Now it should be ICed and keep a reference to x defined on p
1242 "}"
1243 "var result = 0;"
1244 "for (var i = 0; i < 7; i++) {"
1245 " result += o.x;"
1246 "}"
1247 "result");
1248 CHECK_EQ(42 * 7, value->Int32Value(context.local()).FromJust());
1249}
1250
1251
1252// Test the case when we stored callback into
1253// a stub, but it got invalidated later on.
1254THREADED_TEST(InterceptorLoadICInvalidatedCallback) {
1255 v8::Isolate* isolate = CcTest::isolate();
1256 v8::HandleScope scope(isolate);
1257 v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
1258 templ_o->SetHandler(
1259 v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
1260 v8::Local<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate);
1261 templ_p->SetAccessor(v8_str("y"), Return239Callback, SetOnThis);
1262
1263 LocalContext context;
1264 context->Global()
1265 ->Set(context.local(), v8_str("o"),
1266 templ_o->NewInstance(context.local()).ToLocalChecked())
1267 .FromJust();
1268 context->Global()
1269 ->Set(context.local(), v8_str("p"),
1270 templ_p->NewInstance(context.local()).ToLocalChecked())
1271 .FromJust();
1272
1273 v8::Local<Value> value = CompileRun(
1274 "inbetween = new Object();"
1275 "o.__proto__ = inbetween;"
1276 "inbetween.__proto__ = p;"
1277 "for (var i = 0; i < 10; i++) {"
1278 " o.y;"
1279 // Now it should be ICed and keep a reference to y defined on p
1280 "}"
1281 "inbetween.y = 42;"
1282 "var result = 0;"
1283 "for (var i = 0; i < 10; i++) {"
1284 " result += o.y;"
1285 "}"
1286 "result");
1287 CHECK_EQ(42 * 10, value->Int32Value(context.local()).FromJust());
1288}
1289
1290
1291// Test the case when we stored callback into
1292// a stub, but it got invalidated later on due to override on
1293// global object which is between interceptor and callbacks' holders.
1294THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) {
1295 v8::Isolate* isolate = CcTest::isolate();
1296 v8::HandleScope scope(isolate);
1297 v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
1298 templ_o->SetHandler(
1299 v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
1300 v8::Local<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate);
1301 templ_p->SetAccessor(v8_str("y"), Return239Callback, SetOnThis);
1302
1303 LocalContext context;
1304 context->Global()
1305 ->Set(context.local(), v8_str("o"),
1306 templ_o->NewInstance(context.local()).ToLocalChecked())
1307 .FromJust();
1308 context->Global()
1309 ->Set(context.local(), v8_str("p"),
1310 templ_p->NewInstance(context.local()).ToLocalChecked())
1311 .FromJust();
1312
1313 v8::Local<Value> value = CompileRun(
1314 "o.__proto__ = this;"
1315 "this.__proto__ = p;"
1316 "for (var i = 0; i < 10; i++) {"
1317 " if (o.y != 239) throw 'oops: ' + o.y;"
1318 // Now it should be ICed and keep a reference to y defined on p
1319 "}"
1320 "this.y = 42;"
1321 "var result = 0;"
1322 "for (var i = 0; i < 10; i++) {"
1323 " result += o.y;"
1324 "}"
1325 "result");
1326 CHECK_EQ(42 * 10, value->Int32Value(context.local()).FromJust());
1327}
1328
1329// Test load of a non-existing global when a global object has an interceptor.
1330THREADED_TEST(InterceptorLoadGlobalICGlobalWithInterceptor) {
1331 v8::Isolate* isolate = CcTest::isolate();
1332 v8::HandleScope scope(isolate);
1333 v8::Local<v8::ObjectTemplate> templ_global = v8::ObjectTemplate::New(isolate);
1334 templ_global->SetHandler(v8::NamedPropertyHandlerConfiguration(
1335 EmptyInterceptorGetter, EmptyInterceptorSetter));
1336
1337 LocalContext context(nullptr, templ_global);
1338 i::Handle<i::JSReceiver> global_proxy =
1339 v8::Utils::OpenHandle<Object, i::JSReceiver>(context->Global());
1340 CHECK(global_proxy->IsJSGlobalProxy());
1341 i::Handle<i::JSGlobalObject> global(
1342 i::JSGlobalObject::cast(global_proxy->map().prototype()),
1343 global_proxy->GetIsolate());
1344 CHECK(global->map().has_named_interceptor());
1345
1346 v8::Local<Value> value = CompileRun(
1347 "var f = function() { "
1348 " try {"
1349 " x1;"
1350 " } catch(e) {"
1351 " }"
1352 " return typeof x1 === 'undefined';"
1353 "};"
1354 "for (var i = 0; i < 10; i++) {"
1355 " f();"
1356 "};"
1357 "f();");
1358 CHECK(value->BooleanValue(isolate));
1359
1360 value = CompileRun(
1361 "var f = function() { "
1362 " try {"
1363 " x2;"
1364 " return false;"
1365 " } catch(e) {"
1366 " return true;"
1367 " }"
1368 "};"
1369 "for (var i = 0; i < 10; i++) {"
1370 " f();"
1371 "};"
1372 "f();");
1373 CHECK(value->BooleanValue(isolate));
1374
1375 value = CompileRun(
1376 "var f = function() { "
1377 " try {"
1378 " typeof(x3);"
1379 " return true;"
1380 " } catch(e) {"
1381 " return false;"
1382 " }"
1383 "};"
1384 "for (var i = 0; i < 10; i++) {"
1385 " f();"
1386 "};"
1387 "f();");
1388 CHECK(value->BooleanValue(isolate));
1389}
1390
1391// Test load of a non-existing global through prototype chain when a global
1392// object has an interceptor.
1393THREADED_TEST(InterceptorLoadICGlobalWithInterceptor) {
1394 i::FLAG_allow_natives_syntax = true;
1395 v8::Isolate* isolate = CcTest::isolate();
1396 v8::HandleScope scope(isolate);
1397 v8::Local<v8::ObjectTemplate> templ_global = v8::ObjectTemplate::New(isolate);
1398 templ_global->SetHandler(v8::NamedPropertyHandlerConfiguration(
1399 GenericInterceptorGetter, GenericInterceptorSetter));
1400
1401 LocalContext context(nullptr, templ_global);
1402 i::Handle<i::JSReceiver> global_proxy =
1403 v8::Utils::OpenHandle<Object, i::JSReceiver>(context->Global());
1404 CHECK(global_proxy->IsJSGlobalProxy());
1405 i::Handle<i::JSGlobalObject> global(
1406 i::JSGlobalObject::cast(global_proxy->map().prototype()),
1407 global_proxy->GetIsolate());
1408 CHECK(global->map().has_named_interceptor());
1409
1410 ExpectInt32(
1411 "(function() {"
1412 " var f = function(obj) { "
1413 " return obj.foo;"
1414 " };"
1415 " var obj = { __proto__: this, _str_foo: 42 };"
1416 " for (var i = 0; i < 1500; i++) obj['p' + i] = 0;"
1417 " /* Ensure that |obj| is in dictionary mode. */"
1418 " if (%HasFastProperties(obj)) return -1;"
1419 " for (var i = 0; i < 3; i++) {"
1420 " f(obj);"
1421 " };"
1422 " return f(obj);"
1423 "})();",
1424 42);
1425}
1426
1427static void InterceptorLoadICGetter0(
1428 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
1429 ApiTestFuzzer::Fuzz();
1430 CHECK(v8_str("x")
1431 ->Equals(info.GetIsolate()->GetCurrentContext(), name)
1432 .FromJust());
1433 info.GetReturnValue().Set(v8::Integer::New(info.GetIsolate(), 0));
1434}
1435
1436
1437THREADED_TEST(InterceptorReturningZero) {
1438 CheckInterceptorLoadIC(InterceptorLoadICGetter0, "o.x == undefined ? 1 : 0",
1439 0);
1440}
1441
1442namespace {
1443
1444template <typename TKey, v8::internal::PropertyAttributes attribute>
1445void HasICQuery(TKey name, const v8::PropertyCallbackInfo<v8::Integer>& info) {
1446 ApiTestFuzzer::Fuzz();
1447 v8::Isolate* isolate = CcTest::isolate();
1448 CHECK_EQ(isolate, info.GetIsolate());
1449 info.GetReturnValue().Set(v8::Integer::New(isolate, attribute));
1450}
1451
1452template <typename TKey>
1453void HasICQueryToggle(TKey name,
1454 const v8::PropertyCallbackInfo<v8::Integer>& info) {
1455 ApiTestFuzzer::Fuzz();
1456 static bool toggle = false;
1457 toggle = !toggle;
1458 v8::Isolate* isolate = CcTest::isolate();
1459 CHECK_EQ(isolate, info.GetIsolate());
1460 info.GetReturnValue().Set(v8::Integer::New(
1461 isolate, toggle ? v8::internal::ABSENT : v8::internal::NONE));
1462}
1463
1464int named_query_counter = 0;
1465void NamedQueryCallback(Local<Name> name,
1466 const v8::PropertyCallbackInfo<v8::Integer>& info) {
1467 named_query_counter++;
1468}
1469
1470} // namespace
1471
1472THREADED_TEST(InterceptorHasIC) {
1473 named_query_counter = 0;
1474 CheckInterceptorIC(nullptr, NamedQueryCallback,
1475 "var result = 0;"
1476 "for (var i = 0; i < 1000; i++) {"
1477 " 'x' in o;"
1478 "}",
1479 0);
1480 CHECK_EQ(1000, named_query_counter);
1481}
1482
1483THREADED_TEST(InterceptorHasICQueryAbsent) {
1484 CheckInterceptorIC(nullptr, HasICQuery<Local<Name>, v8::internal::ABSENT>,
1485 "var result = 0;"
1486 "for (var i = 0; i < 1000; i++) {"
1487 " if ('x' in o) ++result;"
1488 "}",
1489 0);
1490}
1491
1492THREADED_TEST(InterceptorHasICQueryNone) {
1493 CheckInterceptorIC(nullptr, HasICQuery<Local<Name>, v8::internal::NONE>,
1494 "var result = 0;"
1495 "for (var i = 0; i < 1000; i++) {"
1496 " if ('x' in o) ++result;"
1497 "}",
1498 1000);
1499}
1500
1501THREADED_TEST(InterceptorHasICGetter) {
1502 CheckInterceptorIC(InterceptorLoadICGetter, nullptr,
1503 "var result = 0;"
1504 "for (var i = 0; i < 1000; i++) {"
1505 " if ('x' in o) ++result;"
1506 "}",
1507 1000);
1508}
1509
1510THREADED_TEST(InterceptorHasICQueryGetter) {
1511 CheckInterceptorIC(InterceptorLoadICGetter,
1512 HasICQuery<Local<Name>, v8::internal::ABSENT>,
1513 "var result = 0;"
1514 "for (var i = 0; i < 1000; i++) {"
1515 " if ('x' in o) ++result;"
1516 "}",
1517 0);
1518}
1519
1520THREADED_TEST(InterceptorHasICQueryToggle) {
1521 CheckInterceptorIC(InterceptorLoadICGetter, HasICQueryToggle<Local<Name>>,
1522 "var result = 0;"
1523 "for (var i = 0; i < 1000; i++) {"
1524 " if ('x' in o) ++result;"
1525 "}",
1526 500);
1527}
1528
1529static void InterceptorStoreICSetter(
1530 Local<Name> key, Local<Value> value,
1531 const v8::PropertyCallbackInfo<v8::Value>& info) {
1532 v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
1533 CHECK(v8_str("x")->Equals(context, key).FromJust());
1534 CHECK_EQ(42, value->Int32Value(context).FromJust());
1535 info.GetReturnValue().Set(value);
1536}
1537
1538
1539// This test should hit the store IC for the interceptor case.
1540THREADED_TEST(InterceptorStoreIC) {
1541 v8::Isolate* isolate = CcTest::isolate();
1542 v8::HandleScope scope(isolate);
1543 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
1544 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
1545 InterceptorLoadICGetter, InterceptorStoreICSetter, nullptr, nullptr,
1546 nullptr, v8_str("data")));
1547 LocalContext context;
1548 context->Global()
1549 ->Set(context.local(), v8_str("o"),
1550 templ->NewInstance(context.local()).ToLocalChecked())
1551 .FromJust();
1552 CompileRun(
1553 "for (var i = 0; i < 1000; i++) {"
1554 " o.x = 42;"
1555 "}");
1556}
1557
1558
1559THREADED_TEST(InterceptorStoreICWithNoSetter) {
1560 v8::Isolate* isolate = CcTest::isolate();
1561 v8::HandleScope scope(isolate);
1562 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
1563 templ->SetHandler(
1564 v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
1565 LocalContext context;
1566 context->Global()
1567 ->Set(context.local(), v8_str("o"),
1568 templ->NewInstance(context.local()).ToLocalChecked())
1569 .FromJust();
1570 v8::Local<Value> value = CompileRun(
1571 "for (var i = 0; i < 1000; i++) {"
1572 " o.y = 239;"
1573 "}"
1574 "42 + o.y");
1575 CHECK_EQ(239 + 42, value->Int32Value(context.local()).FromJust());
1576}
1577
1578
1579THREADED_TEST(EmptyInterceptorDoesNotShadowAccessors) {
1580 v8::HandleScope scope(CcTest::isolate());
1581 Local<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate());
1582 Local<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate());
1583 child->Inherit(parent);
1584 AddAccessor(parent, v8_str("age"), SimpleAccessorGetter,
1585 SimpleAccessorSetter);
1586 AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter);
1587 LocalContext env;
1588 env->Global()
1589 ->Set(env.local(), v8_str("Child"),
1590 child->GetFunction(env.local()).ToLocalChecked())
1591 .FromJust();
1592 CompileRun(
1593 "var child = new Child;"
1594 "child.age = 10;");
1595 ExpectBoolean("child.hasOwnProperty('age')", false);
1596 ExpectInt32("child.age", 10);
1597 ExpectInt32("child.accessor_age", 10);
1598}
1599
1600
1601THREADED_TEST(LegacyInterceptorDoesNotSeeSymbols) {
1602 LocalContext env;
1603 v8::Isolate* isolate = CcTest::isolate();
1604 v8::HandleScope scope(isolate);
1605 Local<FunctionTemplate> parent = FunctionTemplate::New(isolate);
1606 Local<FunctionTemplate> child = FunctionTemplate::New(isolate);
1607 v8::Local<v8::Symbol> age = v8::Symbol::New(isolate, v8_str("age"));
1608
1609 child->Inherit(parent);
1610 AddAccessor(parent, age, SymbolAccessorGetter, SymbolAccessorSetter);
1611 AddStringOnlyInterceptor(child, InterceptorGetter, InterceptorSetter);
1612
1613 env->Global()
1614 ->Set(env.local(), v8_str("Child"),
1615 child->GetFunction(env.local()).ToLocalChecked())
1616 .FromJust();
1617 env->Global()->Set(env.local(), v8_str("age"), age).FromJust();
1618 CompileRun(
1619 "var child = new Child;"
1620 "child[age] = 10;");
1621 ExpectInt32("child[age]", 10);
1622 ExpectBoolean("child.hasOwnProperty('age')", false);
1623 ExpectBoolean("child.hasOwnProperty('accessor_age')", true);
1624}
1625
1626
1627THREADED_TEST(GenericInterceptorDoesSeeSymbols) {
1628 LocalContext env;
1629 v8::Isolate* isolate = CcTest::isolate();
1630 v8::HandleScope scope(isolate);
1631 Local<FunctionTemplate> parent = FunctionTemplate::New(isolate);
1632 Local<FunctionTemplate> child = FunctionTemplate::New(isolate);
1633 v8::Local<v8::Symbol> age = v8::Symbol::New(isolate, v8_str("age"));
1634 v8::Local<v8::Symbol> anon = v8::Symbol::New(isolate);
1635
1636 child->Inherit(parent);
1637 AddAccessor(parent, age, SymbolAccessorGetter, SymbolAccessorSetter);
1638 AddInterceptor(child, GenericInterceptorGetter, GenericInterceptorSetter);
1639
1640 env->Global()
1641 ->Set(env.local(), v8_str("Child"),
1642 child->GetFunction(env.local()).ToLocalChecked())
1643 .FromJust();
1644 env->Global()->Set(env.local(), v8_str("age"), age).FromJust();
1645 env->Global()->Set(env.local(), v8_str("anon"), anon).FromJust();
1646 CompileRun(
1647 "var child = new Child;"
1648 "child[age] = 10;");
1649 ExpectInt32("child[age]", 10);
1650 ExpectInt32("child._sym_age", 10);
1651
1652 // Check that it also sees strings.
1653 CompileRun("child.foo = 47");
1654 ExpectInt32("child.foo", 47);
1655 ExpectInt32("child._str_foo", 47);
1656
1657 // Check that the interceptor can punt (in this case, on anonymous symbols).
1658 CompileRun("child[anon] = 31337");
1659 ExpectInt32("child[anon]", 31337);
1660}
1661
1662
1663THREADED_TEST(NamedPropertyHandlerGetter) {
1664 echo_named_call_count = 0;
1665 v8::Isolate* isolate = CcTest::isolate();
1666 v8::HandleScope scope(isolate);
1667 v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
1668 templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
1669 EchoNamedProperty, nullptr, nullptr, nullptr, nullptr, v8_str("data")));
1670 LocalContext env;
1671 env->Global()
1672 ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
1673 .ToLocalChecked()
1674 ->NewInstance(env.local())
1675 .ToLocalChecked())
1676 .FromJust();
1677 CHECK_EQ(0, echo_named_call_count);
1678 v8_compile("obj.x")->Run(env.local()).ToLocalChecked();
1679 CHECK_EQ(1, echo_named_call_count);
1680 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;";
1681 v8::Local<Value> str = CompileRun(code);
1682 String::Utf8Value value(isolate, str);
1683 CHECK_EQ(0, strcmp(*value, "oddlepoddle"));
1684 // Check default behavior
1685 CHECK_EQ(10, v8_compile("obj.flob = 10;")
1686 ->Run(env.local())
1687 .ToLocalChecked()
1688 ->Int32Value(env.local())
1689 .FromJust());
1690 CHECK(v8_compile("'myProperty' in obj")
1691 ->Run(env.local())
1692 .ToLocalChecked()
1693 ->BooleanValue(isolate));
1694 CHECK(v8_compile("delete obj.myProperty")
1695 ->Run(env.local())
1696 .ToLocalChecked()
1697 ->BooleanValue(isolate));
1698}
1699
1700namespace {
1701void NotInterceptingPropertyDefineCallback(
1702 Local<Name> name, const v8::PropertyDescriptor& desc,
1703 const v8::PropertyCallbackInfo<v8::Value>& info) {
1704 // Do not intercept by not calling info.GetReturnValue().Set().
1705}
1706
1707void InterceptingPropertyDefineCallback(
1708 Local<Name> name, const v8::PropertyDescriptor& desc,
1709 const v8::PropertyCallbackInfo<v8::Value>& info) {
1710 // Intercept the callback by setting a non-empty handle
1711 info.GetReturnValue().Set(name);
1712}
1713
1714void CheckDescriptorInDefineCallback(
1715 Local<Name> name, const v8::PropertyDescriptor& desc,
1716 const v8::PropertyCallbackInfo<v8::Value>& info) {
1717 CHECK(!desc.has_writable());
1718 CHECK(!desc.has_value());
1719 CHECK(!desc.has_enumerable());
1720 CHECK(desc.has_configurable());
1721 CHECK(!desc.configurable());
1722 CHECK(desc.has_get());
1723 CHECK(desc.get()->IsFunction());
1724 CHECK(desc.has_set());
1725 CHECK(desc.set()->IsUndefined());
1726 // intercept the callback by setting a non-empty handle
1727 info.GetReturnValue().Set(name);
1728}
1729} // namespace
1730
1731THREADED_TEST(PropertyDefinerCallback) {
1732 v8::HandleScope scope(CcTest::isolate());
1733 LocalContext env;
1734
1735 { // Intercept defineProperty()
1736 v8::Local<v8::FunctionTemplate> templ =
1737 v8::FunctionTemplate::New(CcTest::isolate());
1738 templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
1739 nullptr, nullptr, nullptr, nullptr, nullptr,
1740 NotInterceptingPropertyDefineCallback));
1741 env->Global()
1742 ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
1743 .ToLocalChecked()
1744 ->NewInstance(env.local())
1745 .ToLocalChecked())
1746 .FromJust();
1747 const char* code =
1748 "obj.x = 17; "
1749 "Object.defineProperty(obj, 'x', {value: 42});"
1750 "obj.x;";
1751 CHECK_EQ(42, v8_compile(code)
1752 ->Run(env.local())
1753 .ToLocalChecked()
1754 ->Int32Value(env.local())
1755 .FromJust());
1756 }
1757
1758 { // Intercept defineProperty() for correct accessor descriptor
1759 v8::Local<v8::FunctionTemplate> templ =
1760 v8::FunctionTemplate::New(CcTest::isolate());
1761 templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
1762 nullptr, nullptr, nullptr, nullptr, nullptr,
1763 CheckDescriptorInDefineCallback));
1764 env->Global()
1765 ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
1766 .ToLocalChecked()
1767 ->NewInstance(env.local())
1768 .ToLocalChecked())
1769 .FromJust();
1770 const char* code =
1771 "obj.x = 17; "
1772 "Object.defineProperty(obj, 'x', {"
1773 "get: function(){ return 42; }, "
1774 "set: undefined,"
1775 "configurable: 0"
1776 "});"
1777 "obj.x;";
1778 CHECK_EQ(17, v8_compile(code)
1779 ->Run(env.local())
1780 .ToLocalChecked()
1781 ->Int32Value(env.local())
1782 .FromJust());
1783 }
1784
1785 { // Do not intercept defineProperty()
1786 v8::Local<v8::FunctionTemplate> templ2 =
1787 v8::FunctionTemplate::New(CcTest::isolate());
1788 templ2->InstanceTemplate()->SetHandler(
1789 v8::NamedPropertyHandlerConfiguration(
1790 nullptr, nullptr, nullptr, nullptr, nullptr,
1791 InterceptingPropertyDefineCallback));
1792 env->Global()
1793 ->Set(env.local(), v8_str("obj"), templ2->GetFunction(env.local())
1794 .ToLocalChecked()
1795 ->NewInstance(env.local())
1796 .ToLocalChecked())
1797 .FromJust();
1798
1799 const char* code =
1800 "obj.x = 17; "
1801 "Object.defineProperty(obj, 'x', {value: 42});"
1802 "obj.x;";
1803 CHECK_EQ(17, v8_compile(code)
1804 ->Run(env.local())
1805 .ToLocalChecked()
1806 ->Int32Value(env.local())
1807 .FromJust());
1808 }
1809}
1810
1811namespace {
1812void NotInterceptingPropertyDefineCallbackIndexed(
1813 uint32_t index, const v8::PropertyDescriptor& desc,
1814 const v8::PropertyCallbackInfo<v8::Value>& info) {
1815 // Do not intercept by not calling info.GetReturnValue().Set()
1816}
1817
1818void InterceptingPropertyDefineCallbackIndexed(
1819 uint32_t index, const v8::PropertyDescriptor& desc,
1820 const v8::PropertyCallbackInfo<v8::Value>& info) {
1821 // intercept the callback by setting a non-empty handle
1822 info.GetReturnValue().Set(index);
1823}
1824
1825void CheckDescriptorInDefineCallbackIndexed(
1826 uint32_t index, const v8::PropertyDescriptor& desc,
1827 const v8::PropertyCallbackInfo<v8::Value>& info) {
1828 CHECK(!desc.has_writable());
1829 CHECK(!desc.has_value());
1830 CHECK(desc.has_enumerable());
1831 CHECK(desc.enumerable());
1832 CHECK(!desc.has_configurable());
1833 CHECK(desc.has_get());
1834 CHECK(desc.get()->IsFunction());
1835 CHECK(desc.has_set());
1836 CHECK(desc.set()->IsUndefined());
1837 // intercept the callback by setting a non-empty handle
1838 info.GetReturnValue().Set(index);
1839}
1840} // namespace
1841
1842THREADED_TEST(PropertyDefinerCallbackIndexed) {
1843 v8::HandleScope scope(CcTest::isolate());
1844 LocalContext env;
1845
1846 { // Intercept defineProperty()
1847 v8::Local<v8::FunctionTemplate> templ =
1848 v8::FunctionTemplate::New(CcTest::isolate());
1849 templ->InstanceTemplate()->SetHandler(
1850 v8::IndexedPropertyHandlerConfiguration(
1851 nullptr, nullptr, nullptr, nullptr, nullptr,
1852 NotInterceptingPropertyDefineCallbackIndexed));
1853 env->Global()
1854 ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
1855 .ToLocalChecked()
1856 ->NewInstance(env.local())
1857 .ToLocalChecked())
1858 .FromJust();
1859 const char* code =
1860 "obj[2] = 17; "
1861 "Object.defineProperty(obj, 2, {value: 42});"
1862 "obj[2];";
1863 CHECK_EQ(42, v8_compile(code)
1864 ->Run(env.local())
1865 .ToLocalChecked()
1866 ->Int32Value(env.local())
1867 .FromJust());
1868 }
1869
1870 { // Intercept defineProperty() for correct accessor descriptor
1871 v8::Local<v8::FunctionTemplate> templ =
1872 v8::FunctionTemplate::New(CcTest::isolate());
1873 templ->InstanceTemplate()->SetHandler(
1874 v8::IndexedPropertyHandlerConfiguration(
1875 nullptr, nullptr, nullptr, nullptr, nullptr,
1876 CheckDescriptorInDefineCallbackIndexed));
1877 env->Global()
1878 ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
1879 .ToLocalChecked()
1880 ->NewInstance(env.local())
1881 .ToLocalChecked())
1882 .FromJust();
1883 const char* code =
1884 "obj[2] = 17; "
1885 "Object.defineProperty(obj, 2, {"
1886 "get: function(){ return 42; }, "
1887 "set: undefined,"
1888 "enumerable: true"
1889 "});"
1890 "obj[2];";
1891 CHECK_EQ(17, v8_compile(code)
1892 ->Run(env.local())
1893 .ToLocalChecked()
1894 ->Int32Value(env.local())
1895 .FromJust());
1896 }
1897
1898 { // Do not intercept defineProperty()
1899 v8::Local<v8::FunctionTemplate> templ2 =
1900 v8::FunctionTemplate::New(CcTest::isolate());
1901 templ2->InstanceTemplate()->SetHandler(
1902 v8::IndexedPropertyHandlerConfiguration(
1903 nullptr, nullptr, nullptr, nullptr, nullptr,
1904 InterceptingPropertyDefineCallbackIndexed));
1905 env->Global()
1906 ->Set(env.local(), v8_str("obj"), templ2->GetFunction(env.local())
1907 .ToLocalChecked()
1908 ->NewInstance(env.local())
1909 .ToLocalChecked())
1910 .FromJust();
1911
1912 const char* code =
1913 "obj[2] = 17; "
1914 "Object.defineProperty(obj, 2, {value: 42});"
1915 "obj[2];";
1916 CHECK_EQ(17, v8_compile(code)
1917 ->Run(env.local())
1918 .ToLocalChecked()
1919 ->Int32Value(env.local())
1920 .FromJust());
1921 }
1922}
1923
1924// Test that freeze() is intercepted.
1925THREADED_TEST(PropertyDefinerCallbackForFreeze) {
1926 v8::Isolate* isolate = CcTest::isolate();
1927 v8::HandleScope scope(isolate);
1928 LocalContext env;
1929 v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
1930 templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
1931 nullptr, nullptr, nullptr, nullptr, nullptr,
1932 InterceptingPropertyDefineCallback));
1933 env->Global()
1934 ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
1935 .ToLocalChecked()
1936 ->NewInstance(env.local())
1937 .ToLocalChecked())
1938 .FromJust();
1939 const char* code =
1940 "obj.x = 17; "
1941 "Object.freeze(obj.x); "
1942 "Object.isFrozen(obj.x);";
1943
1944 CHECK(v8_compile(code)
1945 ->Run(env.local())
1946 .ToLocalChecked()
1947 ->BooleanValue(isolate));
1948}
1949
1950// Check that the descriptor passed to the callback is enumerable.
1951namespace {
1952void CheckEnumerablePropertyDefineCallback(
1953 Local<Name> name, const v8::PropertyDescriptor& desc,
1954 const v8::PropertyCallbackInfo<v8::Value>& info) {
1955 CHECK(desc.has_value());
1956 CHECK_EQ(42, desc.value()
1957 ->Int32Value(info.GetIsolate()->GetCurrentContext())
1958 .FromJust());
1959 CHECK(desc.has_enumerable());
1960 CHECK(desc.enumerable());
1961 CHECK(!desc.has_writable());
1962
1963 // intercept the callback by setting a non-empty handle
1964 info.GetReturnValue().Set(name);
1965}
1966} // namespace
1967THREADED_TEST(PropertyDefinerCallbackEnumerable) {
1968 v8::HandleScope scope(CcTest::isolate());
1969 LocalContext env;
1970 v8::Local<v8::FunctionTemplate> templ =
1971 v8::FunctionTemplate::New(CcTest::isolate());
1972 templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
1973 nullptr, nullptr, nullptr, nullptr, nullptr,
1974 CheckEnumerablePropertyDefineCallback));
1975 env->Global()
1976 ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
1977 .ToLocalChecked()
1978 ->NewInstance(env.local())
1979 .ToLocalChecked())
1980 .FromJust();
1981 const char* code =
1982 "obj.x = 17; "
1983 "Object.defineProperty(obj, 'x', {value: 42, enumerable: true});"
1984 "obj.x;";
1985 CHECK_EQ(17, v8_compile(code)
1986 ->Run(env.local())
1987 .ToLocalChecked()
1988 ->Int32Value(env.local())
1989 .FromJust());
1990}
1991
1992// Check that the descriptor passed to the callback is configurable.
1993namespace {
1994void CheckConfigurablePropertyDefineCallback(
1995 Local<Name> name, const v8::PropertyDescriptor& desc,
1996 const v8::PropertyCallbackInfo<v8::Value>& info) {
1997 CHECK(desc.has_value());
1998 CHECK_EQ(42, desc.value()
1999 ->Int32Value(info.GetIsolate()->GetCurrentContext())
2000 .FromJust());
2001 CHECK(desc.has_configurable());
2002 CHECK(desc.configurable());
2003
2004 // intercept the callback by setting a non-empty handle
2005 info.GetReturnValue().Set(name);
2006}
2007} // namespace
2008THREADED_TEST(PropertyDefinerCallbackConfigurable) {
2009 v8::HandleScope scope(CcTest::isolate());
2010 LocalContext env;
2011 v8::Local<v8::FunctionTemplate> templ =
2012 v8::FunctionTemplate::New(CcTest::isolate());
2013 templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
2014 nullptr, nullptr, nullptr, nullptr, nullptr,
2015 CheckConfigurablePropertyDefineCallback));
2016 env->Global()
2017 ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
2018 .ToLocalChecked()
2019 ->NewInstance(env.local())
2020 .ToLocalChecked())
2021 .FromJust();
2022 const char* code =
2023 "obj.x = 17; "
2024 "Object.defineProperty(obj, 'x', {value: 42, configurable: true});"
2025 "obj.x;";
2026 CHECK_EQ(17, v8_compile(code)
2027 ->Run(env.local())
2028 .ToLocalChecked()
2029 ->Int32Value(env.local())
2030 .FromJust());
2031}
2032
2033// Check that the descriptor passed to the callback is writable.
2034namespace {
2035void CheckWritablePropertyDefineCallback(
2036 Local<Name> name, const v8::PropertyDescriptor& desc,
2037 const v8::PropertyCallbackInfo<v8::Value>& info) {
2038 CHECK(desc.has_writable());
2039 CHECK(desc.writable());
2040
2041 // intercept the callback by setting a non-empty handle
2042 info.GetReturnValue().Set(name);
2043}
2044} // namespace
2045THREADED_TEST(PropertyDefinerCallbackWritable) {
2046 v8::HandleScope scope(CcTest::isolate());
2047 LocalContext env;
2048 v8::Local<v8::FunctionTemplate> templ =
2049 v8::FunctionTemplate::New(CcTest::isolate());
2050 templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
2051 nullptr, nullptr, nullptr, nullptr, nullptr,
2052 CheckWritablePropertyDefineCallback));
2053 env->Global()
2054 ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
2055 .ToLocalChecked()
2056 ->NewInstance(env.local())
2057 .ToLocalChecked())
2058 .FromJust();
2059 const char* code =
2060 "obj.x = 17; "
2061 "Object.defineProperty(obj, 'x', {value: 42, writable: true});"
2062 "obj.x;";
2063 CHECK_EQ(17, v8_compile(code)
2064 ->Run(env.local())
2065 .ToLocalChecked()
2066 ->Int32Value(env.local())
2067 .FromJust());
2068}
2069
2070// Check that the descriptor passed to the callback has a getter.
2071namespace {
2072void CheckGetterPropertyDefineCallback(
2073 Local<Name> name, const v8::PropertyDescriptor& desc,
2074 const v8::PropertyCallbackInfo<v8::Value>& info) {
2075 CHECK(desc.has_get());
2076 CHECK(!desc.has_set());
2077 // intercept the callback by setting a non-empty handle
2078 info.GetReturnValue().Set(name);
2079}
2080} // namespace
2081THREADED_TEST(PropertyDefinerCallbackWithGetter) {
2082 v8::HandleScope scope(CcTest::isolate());
2083 LocalContext env;
2084 v8::Local<v8::FunctionTemplate> templ =
2085 v8::FunctionTemplate::New(CcTest::isolate());
2086 templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
2087 nullptr, nullptr, nullptr, nullptr, nullptr,
2088 CheckGetterPropertyDefineCallback));
2089 env->Global()
2090 ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
2091 .ToLocalChecked()
2092 ->NewInstance(env.local())
2093 .ToLocalChecked())
2094 .FromJust();
2095 const char* code =
2096 "obj.x = 17;"
2097 "Object.defineProperty(obj, 'x', {get: function() {return 42;}});"
2098 "obj.x;";
2099 CHECK_EQ(17, v8_compile(code)
2100 ->Run(env.local())
2101 .ToLocalChecked()
2102 ->Int32Value(env.local())
2103 .FromJust());
2104}
2105
2106// Check that the descriptor passed to the callback has a setter.
2107namespace {
2108void CheckSetterPropertyDefineCallback(
2109 Local<Name> name, const v8::PropertyDescriptor& desc,
2110 const v8::PropertyCallbackInfo<v8::Value>& info) {
2111 CHECK(desc.has_set());
2112 CHECK(!desc.has_get());
2113 // intercept the callback by setting a non-empty handle
2114 info.GetReturnValue().Set(name);
2115}
2116} // namespace
2117THREADED_TEST(PropertyDefinerCallbackWithSetter) {
2118 v8::HandleScope scope(CcTest::isolate());
2119 LocalContext env;
2120 v8::Local<v8::FunctionTemplate> templ =
2121 v8::FunctionTemplate::New(CcTest::isolate());
2122 templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
2123 nullptr, nullptr, nullptr, nullptr, nullptr,
2124 CheckSetterPropertyDefineCallback));
2125 env->Global()
2126 ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
2127 .ToLocalChecked()
2128 ->NewInstance(env.local())
2129 .ToLocalChecked())
2130 .FromJust();
2131 const char* code =
2132 "Object.defineProperty(obj, 'x', {set: function() {return 42;}});"
2133 "obj.x = 17;";
2134 CHECK_EQ(17, v8_compile(code)
2135 ->Run(env.local())
2136 .ToLocalChecked()
2137 ->Int32Value(env.local())
2138 .FromJust());
2139}
2140
2141namespace {
2142void EmptyPropertyDescriptorCallback(
2143 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
2144 // Do not intercept by not calling info.GetReturnValue().Set().
2145}
2146
2147void InterceptingPropertyDescriptorCallback(
2148 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
2149 // Intercept the callback by setting a different descriptor.
2150 const char* code =
2151 "var desc = {value: 42};"
2152 "desc;";
2153 Local<Value> descriptor = v8_compile(code)
2154 ->Run(info.GetIsolate()->GetCurrentContext())
2155 .ToLocalChecked();
2156 info.GetReturnValue().Set(descriptor);
2157}
2158} // namespace
2159
2160THREADED_TEST(PropertyDescriptorCallback) {
2161 v8::HandleScope scope(CcTest::isolate());
2162 LocalContext env;
2163
2164 { // Normal behavior of getOwnPropertyDescriptor() with empty callback.
2165 v8::Local<v8::FunctionTemplate> templ =
2166 v8::FunctionTemplate::New(CcTest::isolate());
2167 templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
2168 nullptr, nullptr, EmptyPropertyDescriptorCallback, nullptr, nullptr,
2169 nullptr));
2170 env->Global()
2171 ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
2172 .ToLocalChecked()
2173 ->NewInstance(env.local())
2174 .ToLocalChecked())
2175 .FromJust();
2176 const char* code =
2177 "obj.x = 17; "
2178 "var desc = Object.getOwnPropertyDescriptor(obj, 'x');"
2179 "desc.value;";
2180 CHECK_EQ(17, v8_compile(code)
2181 ->Run(env.local())
2182 .ToLocalChecked()
2183 ->Int32Value(env.local())
2184 .FromJust());
2185 }
2186
2187 { // Intercept getOwnPropertyDescriptor().
2188 v8::Local<v8::FunctionTemplate> templ =
2189 v8::FunctionTemplate::New(CcTest::isolate());
2190 templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
2191 nullptr, nullptr, InterceptingPropertyDescriptorCallback, nullptr,
2192 nullptr, nullptr));
2193 env->Global()
2194 ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
2195 .ToLocalChecked()
2196 ->NewInstance(env.local())
2197 .ToLocalChecked())
2198 .FromJust();
2199 const char* code =
2200 "obj.x = 17; "
2201 "var desc = Object.getOwnPropertyDescriptor(obj, 'x');"
2202 "desc.value;";
2203 CHECK_EQ(42, v8_compile(code)
2204 ->Run(env.local())
2205 .ToLocalChecked()
2206 ->Int32Value(env.local())
2207 .FromJust());
2208 }
2209}
2210
2211namespace {
2212int echo_indexed_call_count = 0;
2213} // namespace
2214
2215static void EchoIndexedProperty(
2216 uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
2217 ApiTestFuzzer::Fuzz();
2218 CHECK(v8_num(637)
2219 ->Equals(info.GetIsolate()->GetCurrentContext(), info.Data())
2220 .FromJust());
2221 echo_indexed_call_count++;
2222 info.GetReturnValue().Set(v8_num(index));
2223}
2224
2225
2226THREADED_TEST(IndexedPropertyHandlerGetter) {
2227 v8::Isolate* isolate = CcTest::isolate();
2228 v8::HandleScope scope(isolate);
2229 v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
2230 templ->InstanceTemplate()->SetHandler(v8::IndexedPropertyHandlerConfiguration(
2231 EchoIndexedProperty, nullptr, nullptr, nullptr, nullptr, v8_num(637)));
2232 LocalContext env;
2233 env->Global()
2234 ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
2235 .ToLocalChecked()
2236 ->NewInstance(env.local())
2237 .ToLocalChecked())
2238 .FromJust();
2239 Local<Script> script = v8_compile("obj[900]");
2240 CHECK_EQ(900, script->Run(env.local())
2241 .ToLocalChecked()
2242 ->Int32Value(env.local())
2243 .FromJust());
2244}
2245
2246
2247THREADED_TEST(PropertyHandlerInPrototype) {
2248 LocalContext env;
2249 v8::Isolate* isolate = env->GetIsolate();
2250 v8::HandleScope scope(isolate);
2251
2252 v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
2253 templ->InstanceTemplate()->SetHandler(v8::IndexedPropertyHandlerConfiguration(
2254 CheckThisIndexedPropertyHandler, CheckThisIndexedPropertySetter,
2255 CheckThisIndexedPropertyQuery, CheckThisIndexedPropertyDeleter,
2256 CheckThisIndexedPropertyEnumerator));
2257
2258 templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
2259 CheckThisNamedPropertyHandler, CheckThisNamedPropertySetter,
2260 CheckThisNamedPropertyQuery, CheckThisNamedPropertyDeleter,
2261 CheckThisNamedPropertyEnumerator));
2262
2263 bottom = templ->GetFunction(env.local())
2264 .ToLocalChecked()
2265 ->NewInstance(env.local())
2266 .ToLocalChecked();
2267 Local<v8::Object> top = templ->GetFunction(env.local())
2268 .ToLocalChecked()
2269 ->NewInstance(env.local())
2270 .ToLocalChecked();
2271 Local<v8::Object> middle = templ->GetFunction(env.local())
2272 .ToLocalChecked()
2273 ->NewInstance(env.local())
2274 .ToLocalChecked();
2275
2276 bottom->SetPrototype(env.local(), middle).FromJust();
2277 middle->SetPrototype(env.local(), top).FromJust();
2278 env->Global()->Set(env.local(), v8_str("obj"), bottom).FromJust();
2279
2280 // Indexed and named get.
2281 CompileRun("obj[0]");
2282 CompileRun("obj.x");
2283
2284 // Indexed and named set.
2285 CompileRun("obj[1] = 42");
2286 CompileRun("obj.y = 42");
2287
2288 // Indexed and named query.
2289 CompileRun("0 in obj");
2290 CompileRun("'x' in obj");
2291
2292 // Indexed and named deleter.
2293 CompileRun("delete obj[0]");
2294 CompileRun("delete obj.x");
2295
2296 // Enumerators.
2297 CompileRun("for (var p in obj) ;");
2298}
2299
2300TEST(PropertyHandlerInPrototypeWithDefine) {
2301 LocalContext env;
2302 v8::Isolate* isolate = env->GetIsolate();
2303 v8::HandleScope scope(isolate);
2304
2305 v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
2306 templ->InstanceTemplate()->SetHandler(v8::IndexedPropertyHandlerConfiguration(
2307 CheckThisIndexedPropertyHandler, CheckThisIndexedPropertySetter,
2308 CheckThisIndexedPropertyDescriptor, CheckThisIndexedPropertyDeleter,
2309 CheckThisIndexedPropertyEnumerator, CheckThisIndexedPropertyDefiner));
2310
2311 templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
2312 CheckThisNamedPropertyHandler, CheckThisNamedPropertySetter,
2313 CheckThisNamedPropertyDescriptor, CheckThisNamedPropertyDeleter,
2314 CheckThisNamedPropertyEnumerator, CheckThisNamedPropertyDefiner));
2315
2316 bottom = templ->GetFunction(env.local())
2317 .ToLocalChecked()
2318 ->NewInstance(env.local())
2319 .ToLocalChecked();
2320 Local<v8::Object> top = templ->GetFunction(env.local())
2321 .ToLocalChecked()
2322 ->NewInstance(env.local())
2323 .ToLocalChecked();
2324 Local<v8::Object> middle = templ->GetFunction(env.local())
2325 .ToLocalChecked()
2326 ->NewInstance(env.local())
2327 .ToLocalChecked();
2328
2329 bottom->SetPrototype(env.local(), middle).FromJust();
2330 middle->SetPrototype(env.local(), top).FromJust();
2331 env->Global()->Set(env.local(), v8_str("obj"), bottom).FromJust();
2332
2333 // Indexed and named get.
2334 CompileRun("obj[0]");
2335 CompileRun("obj.x");
2336
2337 // Indexed and named set.
2338 CompileRun("obj[1] = 42");
2339 CompileRun("obj.y = 42");
2340
2341 // Indexed and named deleter.
2342 CompileRun("delete obj[0]");
2343 CompileRun("delete obj.x");
2344
2345 // Enumerators.
2346 CompileRun("for (var p in obj) ;");
2347
2348 // Indexed and named definer.
2349 CompileRun("Object.defineProperty(obj, 2, {});");
2350 CompileRun("Object.defineProperty(obj, 'z', {});");
2351
2352 // Indexed and named propertyDescriptor.
2353 CompileRun("Object.getOwnPropertyDescriptor(obj, 2);");
2354 CompileRun("Object.getOwnPropertyDescriptor(obj, 'z');");
2355}
2356
2357
2358bool is_bootstrapping = false;
2359static void PrePropertyHandlerGet(
2360 Local<Name> key, const v8::PropertyCallbackInfo<v8::Value>& info) {
2361 ApiTestFuzzer::Fuzz();
2362 if (!is_bootstrapping &&
2363 v8_str("pre")
2364 ->Equals(info.GetIsolate()->GetCurrentContext(), key)
2365 .FromJust()) {
2366 info.GetReturnValue().Set(v8_str("PrePropertyHandler: pre"));
2367 }
2368}
2369
2370
2371static void PrePropertyHandlerQuery(
2372 Local<Name> key, const v8::PropertyCallbackInfo<v8::Integer>& info) {
2373 if (!is_bootstrapping &&
2374 v8_str("pre")
2375 ->Equals(info.GetIsolate()->GetCurrentContext(), key)
2376 .FromJust()) {
2377 info.GetReturnValue().Set(static_cast<int32_t>(v8::None));
2378 }
2379}
2380
2381
2382THREADED_TEST(PrePropertyHandler) {
2383 v8::Isolate* isolate = CcTest::isolate();
2384 v8::HandleScope scope(isolate);
2385 v8::Local<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(isolate);
2386 desc->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
2387 PrePropertyHandlerGet, nullptr, PrePropertyHandlerQuery));
2388 is_bootstrapping = true;
2389 LocalContext env(nullptr, desc->InstanceTemplate());
2390 is_bootstrapping = false;
2391 CompileRun("var pre = 'Object: pre'; var on = 'Object: on';");
2392 v8::Local<Value> result_pre = CompileRun("pre");
2393 CHECK(v8_str("PrePropertyHandler: pre")
2394 ->Equals(env.local(), result_pre)
2395 .FromJust());
2396 v8::Local<Value> result_on = CompileRun("on");
2397 CHECK(v8_str("Object: on")->Equals(env.local(), result_on).FromJust());
2398 v8::Local<Value> result_post = CompileRun("post");
2399 CHECK(result_post.IsEmpty());
2400}
2401
2402
2403THREADED_TEST(EmptyInterceptorBreakTransitions) {
2404 v8::HandleScope scope(CcTest::isolate());
2405 Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
2406 AddInterceptor(templ, EmptyInterceptorGetter, EmptyInterceptorSetter);
2407 LocalContext env;
2408 env->Global()
2409 ->Set(env.local(), v8_str("Constructor"),
2410 templ->GetFunction(env.local()).ToLocalChecked())
2411 .FromJust();
2412 CompileRun(
2413 "var o1 = new Constructor;"
2414 "o1.a = 1;" // Ensure a and x share the descriptor array.
2415 "Object.defineProperty(o1, 'x', {value: 10});");
2416 CompileRun(
2417 "var o2 = new Constructor;"
2418 "o2.a = 1;"
2419 "Object.defineProperty(o2, 'x', {value: 10});");
2420}
2421
2422
2423THREADED_TEST(EmptyInterceptorDoesNotShadowJSAccessors) {
2424 v8::Isolate* isolate = CcTest::isolate();
2425 v8::HandleScope scope(isolate);
2426 Local<FunctionTemplate> parent = FunctionTemplate::New(isolate);
2427 Local<FunctionTemplate> child = FunctionTemplate::New(isolate);
2428 child->Inherit(parent);
2429 AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter);
2430 LocalContext env;
2431 env->Global()
2432 ->Set(env.local(), v8_str("Child"),
2433 child->GetFunction(env.local()).ToLocalChecked())
2434 .FromJust();
2435 CompileRun(
2436 "var child = new Child;"
2437 "var parent = child.__proto__;"
2438 "Object.defineProperty(parent, 'age', "
2439 " {get: function(){ return this.accessor_age; }, "
2440 " set: function(v){ this.accessor_age = v; }, "
2441 " enumerable: true, configurable: true});"
2442 "child.age = 10;");
2443 ExpectBoolean("child.hasOwnProperty('age')", false);
2444 ExpectInt32("child.age", 10);
2445 ExpectInt32("child.accessor_age", 10);
2446}
2447
2448
2449THREADED_TEST(EmptyInterceptorDoesNotShadowApiAccessors) {
2450 v8::Isolate* isolate = CcTest::isolate();
2451 v8::HandleScope scope(isolate);
2452 Local<FunctionTemplate> parent = FunctionTemplate::New(isolate);
2453 auto returns_42 = FunctionTemplate::New(isolate, Returns42);
2454 parent->PrototypeTemplate()->SetAccessorProperty(v8_str("age"), returns_42);
2455 Local<FunctionTemplate> child = FunctionTemplate::New(isolate);
2456 child->Inherit(parent);
2457 AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter);
2458 LocalContext env;
2459 env->Global()
2460 ->Set(env.local(), v8_str("Child"),
2461 child->GetFunction(env.local()).ToLocalChecked())
2462 .FromJust();
2463 CompileRun(
2464 "var child = new Child;"
2465 "var parent = child.__proto__;");
2466 ExpectBoolean("child.hasOwnProperty('age')", false);
2467 ExpectInt32("child.age", 42);
2468 // Check interceptor followup.
2469 ExpectInt32(
2470 "var result;"
2471 "for (var i = 0; i < 4; ++i) {"
2472 " result = child.age;"
2473 "}"
2474 "result",
2475 42);
2476}
2477
2478
2479THREADED_TEST(EmptyInterceptorDoesNotAffectJSProperties) {
2480 v8::Isolate* isolate = CcTest::isolate();
2481 v8::HandleScope scope(isolate);
2482 Local<FunctionTemplate> parent = FunctionTemplate::New(isolate);
2483 Local<FunctionTemplate> child = FunctionTemplate::New(isolate);
2484 child->Inherit(parent);
2485 AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter);
2486 LocalContext env;
2487 env->Global()
2488 ->Set(env.local(), v8_str("Child"),
2489 child->GetFunction(env.local()).ToLocalChecked())
2490 .FromJust();
2491 CompileRun(
2492 "var child = new Child;"
2493 "var parent = child.__proto__;"
2494 "parent.name = 'Alice';");
2495 ExpectBoolean("child.hasOwnProperty('name')", false);
2496 ExpectString("child.name", "Alice");
2497 CompileRun("child.name = 'Bob';");
2498 ExpectString("child.name", "Bob");
2499 ExpectBoolean("child.hasOwnProperty('name')", true);
2500 ExpectString("parent.name", "Alice");
2501}
2502
2503
2504THREADED_TEST(SwitchFromInterceptorToAccessor) {
2505 v8::HandleScope scope(CcTest::isolate());
2506 Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
2507 AddAccessor(templ, v8_str("age"), SimpleAccessorGetter, SimpleAccessorSetter);
2508 AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
2509 LocalContext env;
2510 env->Global()
2511 ->Set(env.local(), v8_str("Obj"),
2512 templ->GetFunction(env.local()).ToLocalChecked())
2513 .FromJust();
2514 CompileRun(
2515 "var obj = new Obj;"
2516 "function setAge(i){ obj.age = i; };"
2517 "for(var i = 0; i <= 10000; i++) setAge(i);");
2518 // All i < 10000 go to the interceptor.
2519 ExpectInt32("obj.interceptor_age", 9999);
2520 // The last i goes to the accessor.
2521 ExpectInt32("obj.accessor_age", 10000);
2522}
2523
2524
2525THREADED_TEST(SwitchFromAccessorToInterceptor) {
2526 v8::HandleScope scope(CcTest::isolate());
2527 Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
2528 AddAccessor(templ, v8_str("age"), SimpleAccessorGetter, SimpleAccessorSetter);
2529 AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
2530 LocalContext env;
2531 env->Global()
2532 ->Set(env.local(), v8_str("Obj"),
2533 templ->GetFunction(env.local()).ToLocalChecked())
2534 .FromJust();
2535 CompileRun(
2536 "var obj = new Obj;"
2537 "function setAge(i){ obj.age = i; };"
2538 "for(var i = 20000; i >= 9999; i--) setAge(i);");
2539 // All i >= 10000 go to the accessor.
2540 ExpectInt32("obj.accessor_age", 10000);
2541 // The last i goes to the interceptor.
2542 ExpectInt32("obj.interceptor_age", 9999);
2543}
2544
2545
2546THREADED_TEST(SwitchFromInterceptorToAccessorWithInheritance) {
2547 v8::HandleScope scope(CcTest::isolate());
2548 Local<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate());
2549 Local<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate());
2550 child->Inherit(parent);
2551 AddAccessor(parent, v8_str("age"), SimpleAccessorGetter,
2552 SimpleAccessorSetter);
2553 AddInterceptor(child, InterceptorGetter, InterceptorSetter);
2554 LocalContext env;
2555 env->Global()
2556 ->Set(env.local(), v8_str("Child"),
2557 child->GetFunction(env.local()).ToLocalChecked())
2558 .FromJust();
2559 CompileRun(
2560 "var child = new Child;"
2561 "function setAge(i){ child.age = i; };"
2562 "for(var i = 0; i <= 10000; i++) setAge(i);");
2563 // All i < 10000 go to the interceptor.
2564 ExpectInt32("child.interceptor_age", 9999);
2565 // The last i goes to the accessor.
2566 ExpectInt32("child.accessor_age", 10000);
2567}
2568
2569
2570THREADED_TEST(SwitchFromAccessorToInterceptorWithInheritance) {
2571 v8::HandleScope scope(CcTest::isolate());
2572 Local<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate());
2573 Local<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate());
2574 child->Inherit(parent);
2575 AddAccessor(parent, v8_str("age"), SimpleAccessorGetter,
2576 SimpleAccessorSetter);
2577 AddInterceptor(child, InterceptorGetter, InterceptorSetter);
2578 LocalContext env;
2579 env->Global()
2580 ->Set(env.local(), v8_str("Child"),
2581 child->GetFunction(env.local()).ToLocalChecked())
2582 .FromJust();
2583 CompileRun(
2584 "var child = new Child;"
2585 "function setAge(i){ child.age = i; };"
2586 "for(var i = 20000; i >= 9999; i--) setAge(i);");
2587 // All i >= 10000 go to the accessor.
2588 ExpectInt32("child.accessor_age", 10000);
2589 // The last i goes to the interceptor.
2590 ExpectInt32("child.interceptor_age", 9999);
2591}
2592
2593
2594THREADED_TEST(SwitchFromInterceptorToJSAccessor) {
2595 v8::HandleScope scope(CcTest::isolate());
2596 Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
2597 AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
2598 LocalContext env;
2599 env->Global()
2600 ->Set(env.local(), v8_str("Obj"),
2601 templ->GetFunction(env.local()).ToLocalChecked())
2602 .FromJust();
2603 CompileRun(
2604 "var obj = new Obj;"
2605 "function setter(i) { this.accessor_age = i; };"
2606 "function getter() { return this.accessor_age; };"
2607 "function setAge(i) { obj.age = i; };"
2608 "Object.defineProperty(obj, 'age', { get:getter, set:setter });"
2609 "for(var i = 0; i <= 10000; i++) setAge(i);");
2610 // All i < 10000 go to the interceptor.
2611 ExpectInt32("obj.interceptor_age", 9999);
2612 // The last i goes to the JavaScript accessor.
2613 ExpectInt32("obj.accessor_age", 10000);
2614 // The installed JavaScript getter is still intact.
2615 // This last part is a regression test for issue 1651 and relies on the fact
2616 // that both interceptor and accessor are being installed on the same object.
2617 ExpectInt32("obj.age", 10000);
2618 ExpectBoolean("obj.hasOwnProperty('age')", true);
2619 ExpectUndefined("Object.getOwnPropertyDescriptor(obj, 'age').value");
2620}
2621
2622
2623THREADED_TEST(SwitchFromJSAccessorToInterceptor) {
2624 v8::HandleScope scope(CcTest::isolate());
2625 Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
2626 AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
2627 LocalContext env;
2628 env->Global()
2629 ->Set(env.local(), v8_str("Obj"),
2630 templ->GetFunction(env.local()).ToLocalChecked())
2631 .FromJust();
2632 CompileRun(
2633 "var obj = new Obj;"
2634 "function setter(i) { this.accessor_age = i; };"
2635 "function getter() { return this.accessor_age; };"
2636 "function setAge(i) { obj.age = i; };"
2637 "Object.defineProperty(obj, 'age', { get:getter, set:setter });"
2638 "for(var i = 20000; i >= 9999; i--) setAge(i);");
2639 // All i >= 10000 go to the accessor.
2640 ExpectInt32("obj.accessor_age", 10000);
2641 // The last i goes to the interceptor.
2642 ExpectInt32("obj.interceptor_age", 9999);
2643 // The installed JavaScript getter is still intact.
2644 // This last part is a regression test for issue 1651 and relies on the fact
2645 // that both interceptor and accessor are being installed on the same object.
2646 ExpectInt32("obj.age", 10000);
2647 ExpectBoolean("obj.hasOwnProperty('age')", true);
2648 ExpectUndefined("Object.getOwnPropertyDescriptor(obj, 'age').value");
2649}
2650
2651
2652THREADED_TEST(SwitchFromInterceptorToProperty) {
2653 v8::HandleScope scope(CcTest::isolate());
2654 Local<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate());
2655 Local<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate());
2656 child->Inherit(parent);
2657 AddInterceptor(child, InterceptorGetter, InterceptorSetter);
2658 LocalContext env;
2659 env->Global()
2660 ->Set(env.local(), v8_str("Child"),
2661 child->GetFunction(env.local()).ToLocalChecked())
2662 .FromJust();
2663 CompileRun(
2664 "var child = new Child;"
2665 "function setAge(i){ child.age = i; };"
2666 "for(var i = 0; i <= 10000; i++) setAge(i);");
2667 // All i < 10000 go to the interceptor.
2668 ExpectInt32("child.interceptor_age", 9999);
2669 // The last i goes to child's own property.
2670 ExpectInt32("child.age", 10000);
2671}
2672
2673
2674THREADED_TEST(SwitchFromPropertyToInterceptor) {
2675 v8::HandleScope scope(CcTest::isolate());
2676 Local<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate());
2677 Local<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate());
2678 child->Inherit(parent);
2679 AddInterceptor(child, InterceptorGetter, InterceptorSetter);
2680 LocalContext env;
2681 env->Global()
2682 ->Set(env.local(), v8_str("Child"),
2683 child->GetFunction(env.local()).ToLocalChecked())
2684 .FromJust();
2685 CompileRun(
2686 "var child = new Child;"
2687 "function setAge(i){ child.age = i; };"
2688 "for(var i = 20000; i >= 9999; i--) setAge(i);");
2689 // All i >= 10000 go to child's own property.
2690 ExpectInt32("child.age", 10000);
2691 // The last i goes to the interceptor.
2692 ExpectInt32("child.interceptor_age", 9999);
2693}
2694
2695
2696static bool interceptor_for_hidden_properties_called;
2697static void InterceptorForHiddenProperties(
2698 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
2699 interceptor_for_hidden_properties_called = true;
2700}
2701
2702THREADED_TEST(NoSideEffectPropertyHandler) {
2703 v8::Isolate* isolate = CcTest::isolate();
2704 v8::HandleScope scope(isolate);
2705 LocalContext context;
2706
2707 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
2708 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
2709 EmptyInterceptorGetter, EmptyInterceptorSetter, EmptyInterceptorQuery,
2710 EmptyInterceptorDeleter, EmptyInterceptorEnumerator));
2711 v8::Local<v8::Object> object =
2712 templ->NewInstance(context.local()).ToLocalChecked();
2713 context->Global()->Set(context.local(), v8_str("obj"), object).FromJust();
2714
2715 CHECK(v8::debug::EvaluateGlobal(
2716 isolate, v8_str("obj.x"),
2717 v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
2718 .IsEmpty());
2719 CHECK(v8::debug::EvaluateGlobal(
2720 isolate, v8_str("obj.x = 1"),
2721 v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
2722 .IsEmpty());
2723 CHECK(v8::debug::EvaluateGlobal(
2724 isolate, v8_str("'x' in obj"),
2725 v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
2726 .IsEmpty());
2727 CHECK(v8::debug::EvaluateGlobal(
2728 isolate, v8_str("delete obj.x"),
2729 v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
2730 .IsEmpty());
2731 // Wrap the variable declaration since declaring globals is a side effect.
2732 CHECK(v8::debug::EvaluateGlobal(
2733 isolate, v8_str("(function() { for (var p in obj) ; })()"),
2734 v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
2735 .IsEmpty());
2736
2737 // Side-effect-free version.
2738 Local<ObjectTemplate> templ2 = ObjectTemplate::New(isolate);
2739 templ2->SetHandler(v8::NamedPropertyHandlerConfiguration(
2740 EmptyInterceptorGetter, EmptyInterceptorSetter, EmptyInterceptorQuery,
2741 EmptyInterceptorDeleter, EmptyInterceptorEnumerator,
2742 v8::Local<v8::Value>(), v8::PropertyHandlerFlags::kHasNoSideEffect));
2743 v8::Local<v8::Object> object2 =
2744 templ2->NewInstance(context.local()).ToLocalChecked();
2745 context->Global()->Set(context.local(), v8_str("obj2"), object2).FromJust();
2746
2747 v8::debug::EvaluateGlobal(
2748 isolate, v8_str("obj2.x"),
2749 v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
2750 .ToLocalChecked();
2751 CHECK(v8::debug::EvaluateGlobal(
2752 isolate, v8_str("obj2.x = 1"),
2753 v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
2754 .IsEmpty());
2755 v8::debug::EvaluateGlobal(
2756 isolate, v8_str("'x' in obj2"),
2757 v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
2758 .ToLocalChecked();
2759 CHECK(v8::debug::EvaluateGlobal(
2760 isolate, v8_str("delete obj2.x"),
2761 v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
2762 .IsEmpty());
2763 v8::debug::EvaluateGlobal(
2764 isolate, v8_str("(function() { for (var p in obj2) ; })()"),
2765 v8::debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect)
2766 .ToLocalChecked();
2767}
2768
2769THREADED_TEST(HiddenPropertiesWithInterceptors) {
2770 LocalContext context;
2771 v8::Isolate* isolate = context->GetIsolate();
2772 v8::HandleScope scope(isolate);
2773
2774 interceptor_for_hidden_properties_called = false;
2775
2776 v8::Local<v8::Private> key =
2777 v8::Private::New(isolate, v8_str("api-test::hidden-key"));
2778
2779 // Associate an interceptor with an object and start setting hidden values.
2780 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(isolate);
2781 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
2782 instance_templ->SetHandler(
2783 v8::NamedPropertyHandlerConfiguration(InterceptorForHiddenProperties));
2784 Local<v8::Function> function =
2785 fun_templ->GetFunction(context.local()).ToLocalChecked();
2786 Local<v8::Object> obj =
2787 function->NewInstance(context.local()).ToLocalChecked();
2788 CHECK(obj->SetPrivate(context.local(), key, v8::Integer::New(isolate, 2302))
2789 .FromJust());
2790 CHECK_EQ(2302, obj->GetPrivate(context.local(), key)
2791 .ToLocalChecked()
2792 ->Int32Value(context.local())
2793 .FromJust());
2794 CHECK(!interceptor_for_hidden_properties_called);
2795}
2796
2797
2798static void XPropertyGetter(Local<Name> property,
2799 const v8::PropertyCallbackInfo<v8::Value>& info) {
2800 ApiTestFuzzer::Fuzz();
2801 CHECK(info.Data()->IsUndefined());
2802 info.GetReturnValue().Set(property);
2803}
2804
2805
2806THREADED_TEST(NamedInterceptorPropertyRead) {
2807 v8::Isolate* isolate = CcTest::isolate();
2808 v8::HandleScope scope(isolate);
2809 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
2810 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(XPropertyGetter));
2811 LocalContext context;
2812 context->Global()
2813 ->Set(context.local(), v8_str("obj"),
2814 templ->NewInstance(context.local()).ToLocalChecked())
2815 .FromJust();
2816 Local<Script> script = v8_compile("obj.x");
2817 for (int i = 0; i < 10; i++) {
2818 Local<Value> result = script->Run(context.local()).ToLocalChecked();
2819 CHECK(result->Equals(context.local(), v8_str("x")).FromJust());
2820 }
2821}
2822
2823
2824THREADED_TEST(NamedInterceptorDictionaryIC) {
2825 v8::Isolate* isolate = CcTest::isolate();
2826 v8::HandleScope scope(isolate);
2827 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
2828 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(XPropertyGetter));
2829 LocalContext context;
2830 // Create an object with a named interceptor.
2831 context->Global()
2832 ->Set(context.local(), v8_str("interceptor_obj"),
2833 templ->NewInstance(context.local()).ToLocalChecked())
2834 .FromJust();
2835 Local<Script> script = v8_compile("interceptor_obj.x");
2836 for (int i = 0; i < 10; i++) {
2837 Local<Value> result = script->Run(context.local()).ToLocalChecked();
2838 CHECK(result->Equals(context.local(), v8_str("x")).FromJust());
2839 }
2840 // Create a slow case object and a function accessing a property in
2841 // that slow case object (with dictionary probing in generated
2842 // code). Then force object with a named interceptor into slow-case,
2843 // pass it to the function, and check that the interceptor is called
2844 // instead of accessing the local property.
2845 Local<Value> result = CompileRun(
2846 "function get_x(o) { return o.x; };"
2847 "var obj = { x : 42, y : 0 };"
2848 "delete obj.y;"
2849 "for (var i = 0; i < 10; i++) get_x(obj);"
2850 "interceptor_obj.x = 42;"
2851 "interceptor_obj.y = 10;"
2852 "delete interceptor_obj.y;"
2853 "get_x(interceptor_obj)");
2854 CHECK(result->Equals(context.local(), v8_str("x")).FromJust());
2855}
2856
2857
2858THREADED_TEST(NamedInterceptorDictionaryICMultipleContext) {
2859 v8::Isolate* isolate = CcTest::isolate();
2860 v8::HandleScope scope(isolate);
2861 v8::Local<Context> context1 = Context::New(isolate);
2862
2863 context1->Enter();
2864 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
2865 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(XPropertyGetter));
2866 // Create an object with a named interceptor.
2867 v8::Local<v8::Object> object = templ->NewInstance(context1).ToLocalChecked();
2868 context1->Global()
2869 ->Set(context1, v8_str("interceptor_obj"), object)
2870 .FromJust();
2871
2872 // Force the object into the slow case.
2873 CompileRun(
2874 "interceptor_obj.y = 0;"
2875 "delete interceptor_obj.y;");
2876 context1->Exit();
2877
2878 {
2879 // Introduce the object into a different context.
2880 // Repeat named loads to exercise ICs.
2881 LocalContext context2;
2882 context2->Global()
2883 ->Set(context2.local(), v8_str("interceptor_obj"), object)
2884 .FromJust();
2885 Local<Value> result = CompileRun(
2886 "function get_x(o) { return o.x; }"
2887 "interceptor_obj.x = 42;"
2888 "for (var i=0; i != 10; i++) {"
2889 " get_x(interceptor_obj);"
2890 "}"
2891 "get_x(interceptor_obj)");
2892 // Check that the interceptor was actually invoked.
2893 CHECK(result->Equals(context2.local(), v8_str("x")).FromJust());
2894 }
2895
2896 // Return to the original context and force some object to the slow case
2897 // to cause the NormalizedMapCache to verify.
2898 context1->Enter();
2899 CompileRun("var obj = { x : 0 }; delete obj.x;");
2900 context1->Exit();
2901}
2902
2903
2904static void SetXOnPrototypeGetter(
2905 Local<Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
2906 // Set x on the prototype object and do not handle the get request.
2907 v8::Local<v8::Value> proto = info.Holder()->GetPrototype();
2908 proto.As<v8::Object>()
2909 ->Set(info.GetIsolate()->GetCurrentContext(), v8_str("x"),
2910 v8::Integer::New(info.GetIsolate(), 23))
2911 .FromJust();
2912}
2913
2914
2915// This is a regression test for http://crbug.com/20104. Map
2916// transitions should not interfere with post interceptor lookup.
2917THREADED_TEST(NamedInterceptorMapTransitionRead) {
2918 v8::Isolate* isolate = CcTest::isolate();
2919 v8::HandleScope scope(isolate);
2920 Local<v8::FunctionTemplate> function_template =
2921 v8::FunctionTemplate::New(isolate);
2922 Local<v8::ObjectTemplate> instance_template =
2923 function_template->InstanceTemplate();
2924 instance_template->SetHandler(
2925 v8::NamedPropertyHandlerConfiguration(SetXOnPrototypeGetter));
2926 LocalContext context;
2927 context->Global()
2928 ->Set(context.local(), v8_str("F"),
2929 function_template->GetFunction(context.local()).ToLocalChecked())
2930 .FromJust();
2931 // Create an instance of F and introduce a map transition for x.
2932 CompileRun("var o = new F(); o.x = 23;");
2933 // Create an instance of F and invoke the getter. The result should be 23.
2934 Local<Value> result = CompileRun("o = new F(); o.x");
2935 CHECK_EQ(23, result->Int32Value(context.local()).FromJust());
2936}
2937
2938
2939static void IndexedPropertyGetter(
2940 uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
2941 ApiTestFuzzer::Fuzz();
2942 if (index == 37) {
2943 info.GetReturnValue().Set(v8_num(625));
2944 }
2945}
2946
2947
2948static void IndexedPropertySetter(
2949 uint32_t index, Local<Value> value,
2950 const v8::PropertyCallbackInfo<v8::Value>& info) {
2951 ApiTestFuzzer::Fuzz();
2952 if (index == 39) {
2953 info.GetReturnValue().Set(value);
2954 }
2955}
2956
2957
2958THREADED_TEST(IndexedInterceptorWithIndexedAccessor) {
2959 v8::Isolate* isolate = CcTest::isolate();
2960 v8::HandleScope scope(isolate);
2961 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
2962 templ->SetHandler(v8::IndexedPropertyHandlerConfiguration(
2963 IndexedPropertyGetter, IndexedPropertySetter));
2964 LocalContext context;
2965 context->Global()
2966 ->Set(context.local(), v8_str("obj"),
2967 templ->NewInstance(context.local()).ToLocalChecked())
2968 .FromJust();
2969 Local<Script> getter_script =
2970 v8_compile("obj.__defineGetter__(\"3\", function(){return 5;});obj[3];");
2971 Local<Script> setter_script = v8_compile(
2972 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});"
2973 "obj[17] = 23;"
2974 "obj.foo;");
2975 Local<Script> interceptor_setter_script = v8_compile(
2976 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});"
2977 "obj[39] = 47;"
2978 "obj.foo;"); // This setter should not run, due to the interceptor.
2979 Local<Script> interceptor_getter_script = v8_compile("obj[37];");
2980 Local<Value> result = getter_script->Run(context.local()).ToLocalChecked();
2981 CHECK(v8_num(5)->Equals(context.local(), result).FromJust());
2982 result = setter_script->Run(context.local()).ToLocalChecked();
2983 CHECK(v8_num(23)->Equals(context.local(), result).FromJust());
2984 result = interceptor_setter_script->Run(context.local()).ToLocalChecked();
2985 CHECK(v8_num(23)->Equals(context.local(), result).FromJust());
2986 result = interceptor_getter_script->Run(context.local()).ToLocalChecked();
2987 CHECK(v8_num(625)->Equals(context.local(), result).FromJust());
2988}
2989
2990
2991static void UnboxedDoubleIndexedPropertyGetter(
2992 uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
2993 ApiTestFuzzer::Fuzz();
2994 if (index < 25) {
2995 info.GetReturnValue().Set(v8_num(index));
2996 }
2997}
2998
2999
3000static void UnboxedDoubleIndexedPropertySetter(
3001 uint32_t index, Local<Value> value,
3002 const v8::PropertyCallbackInfo<v8::Value>& info) {
3003 ApiTestFuzzer::Fuzz();
3004 if (index < 25) {
3005 info.GetReturnValue().Set(v8_num(index));
3006 }
3007}
3008
3009
3010void UnboxedDoubleIndexedPropertyEnumerator(
3011 const v8::PropertyCallbackInfo<v8::Array>& info) {
3012 // Force the list of returned keys to be stored in a FastDoubleArray.
3013 Local<Script> indexed_property_names_script = v8_compile(
3014 "keys = new Array(); keys[125000] = 1;"
3015 "for(i = 0; i < 80000; i++) { keys[i] = i; };"
3016 "keys.length = 25; keys;");
3017 Local<Value> result =
3018 indexed_property_names_script->Run(info.GetIsolate()->GetCurrentContext())
3019 .ToLocalChecked();
3020 info.GetReturnValue().Set(Local<v8::Array>::Cast(result));
3021}
3022
3023
3024// Make sure that the the interceptor code in the runtime properly handles
3025// merging property name lists for double-array-backed arrays.
3026THREADED_TEST(IndexedInterceptorUnboxedDoubleWithIndexedAccessor) {
3027 v8::Isolate* isolate = CcTest::isolate();
3028 v8::HandleScope scope(isolate);
3029 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
3030 templ->SetHandler(v8::IndexedPropertyHandlerConfiguration(
3031 UnboxedDoubleIndexedPropertyGetter, UnboxedDoubleIndexedPropertySetter,
3032 nullptr, nullptr, UnboxedDoubleIndexedPropertyEnumerator));
3033 LocalContext context;
3034 context->Global()
3035 ->Set(context.local(), v8_str("obj"),
3036 templ->NewInstance(context.local()).ToLocalChecked())
3037 .FromJust();
3038 // When obj is created, force it to be Stored in a FastDoubleArray.
3039 Local<Script> create_unboxed_double_script = v8_compile(
3040 "obj[125000] = 1; for(i = 0; i < 80000; i+=2) { obj[i] = i; } "
3041 "key_count = 0; "
3042 "for (x in obj) {key_count++;};"
3043 "obj;");
3044 Local<Value> result =
3045 create_unboxed_double_script->Run(context.local()).ToLocalChecked();
3046 CHECK(result->ToObject(context.local())
3047 .ToLocalChecked()
3048 ->HasRealIndexedProperty(context.local(), 2000)
3049 .FromJust());
3050 Local<Script> key_count_check = v8_compile("key_count;");
3051 result = key_count_check->Run(context.local()).ToLocalChecked();
3052 CHECK(v8_num(40013)->Equals(context.local(), result).FromJust());
3053}
3054
3055
3056void SloppyArgsIndexedPropertyEnumerator(
3057 const v8::PropertyCallbackInfo<v8::Array>& info) {
3058 // Force the list of returned keys to be stored in a Arguments object.
3059 Local<Script> indexed_property_names_script = v8_compile(
3060 "function f(w,x) {"
3061 " return arguments;"
3062 "}"
3063 "keys = f(0, 1, 2, 3);"
3064 "keys;");
3065 Local<Object> result = Local<Object>::Cast(
3066 indexed_property_names_script->Run(info.GetIsolate()->GetCurrentContext())
3067 .ToLocalChecked());
3068 // Have to populate the handle manually, as it's not Cast-able.
3069 i::Handle<i::JSReceiver> o =
3070 v8::Utils::OpenHandle<Object, i::JSReceiver>(result);
3071 i::Handle<i::JSArray> array(i::JSArray::unchecked_cast(*o), o->GetIsolate());
3072 info.GetReturnValue().Set(v8::Utils::ToLocal(array));
3073}
3074
3075
3076static void SloppyIndexedPropertyGetter(
3077 uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
3078 ApiTestFuzzer::Fuzz();
3079 if (index < 4) {
3080 info.GetReturnValue().Set(v8_num(index));
3081 }
3082}
3083
3084
3085// Make sure that the the interceptor code in the runtime properly handles
3086// merging property name lists for non-string arguments arrays.
3087THREADED_TEST(IndexedInterceptorSloppyArgsWithIndexedAccessor) {
3088 v8::Isolate* isolate = CcTest::isolate();
3089 v8::HandleScope scope(isolate);
3090 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
3091 templ->SetHandler(v8::IndexedPropertyHandlerConfiguration(
3092 SloppyIndexedPropertyGetter, nullptr, nullptr, nullptr,
3093 SloppyArgsIndexedPropertyEnumerator));
3094 LocalContext context;
3095 context->Global()
3096 ->Set(context.local(), v8_str("obj"),
3097 templ->NewInstance(context.local()).ToLocalChecked())
3098 .FromJust();
3099 Local<Script> create_args_script = v8_compile(
3100 "var key_count = 0;"
3101 "for (x in obj) {key_count++;} key_count;");
3102 Local<Value> result =
3103 create_args_script->Run(context.local()).ToLocalChecked();
3104 CHECK(v8_num(4)->Equals(context.local(), result).FromJust());
3105}
3106
3107
3108static void IdentityIndexedPropertyGetter(
3109 uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
3110 info.GetReturnValue().Set(index);
3111}
3112
3113
3114THREADED_TEST(IndexedInterceptorWithGetOwnPropertyDescriptor) {
3115 v8::Isolate* isolate = CcTest::isolate();
3116 v8::HandleScope scope(isolate);
3117 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
3118 templ->SetHandler(
3119 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
3120
3121 LocalContext context;
3122 context->Global()
3123 ->Set(context.local(), v8_str("obj"),
3124 templ->NewInstance(context.local()).ToLocalChecked())
3125 .FromJust();
3126
3127 // Check fast object case.
3128 const char* fast_case_code =
3129 "Object.getOwnPropertyDescriptor(obj, 0).value.toString()";
3130 ExpectString(fast_case_code, "0");
3131
3132 // Check slow case.
3133 const char* slow_case_code =
3134 "obj.x = 1; delete obj.x;"
3135 "Object.getOwnPropertyDescriptor(obj, 1).value.toString()";
3136 ExpectString(slow_case_code, "1");
3137}
3138
3139
3140THREADED_TEST(IndexedInterceptorWithNoSetter) {
3141 v8::Isolate* isolate = CcTest::isolate();
3142 v8::HandleScope scope(isolate);
3143 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
3144 templ->SetHandler(
3145 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
3146
3147 LocalContext context;
3148 context->Global()
3149 ->Set(context.local(), v8_str("obj"),
3150 templ->NewInstance(context.local()).ToLocalChecked())
3151 .FromJust();
3152
3153 const char* code =
3154 "try {"
3155 " obj[0] = 239;"
3156 " for (var i = 0; i < 100; i++) {"
3157 " var v = obj[0];"
3158 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;"
3159 " }"
3160 " 'PASSED'"
3161 "} catch(e) {"
3162 " e"
3163 "}";
3164 ExpectString(code, "PASSED");
3165}
3166
3167static bool AccessAlwaysBlocked(Local<v8::Context> accessing_context,
3168 Local<v8::Object> accessed_object,
3169 Local<v8::Value> data) {
3170 return false;
3171}
3172
3173
3174THREADED_TEST(IndexedInterceptorWithAccessorCheck) {
3175 v8::Isolate* isolate = CcTest::isolate();
3176 v8::HandleScope scope(isolate);
3177 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
3178 templ->SetHandler(
3179 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
3180
3181 templ->SetAccessCheckCallback(AccessAlwaysBlocked);
3182
3183 LocalContext context;
3184 Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked();
3185 context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust();
3186
3187 const char* code =
3188 "var result = 'PASSED';"
3189 "for (var i = 0; i < 100; i++) {"
3190 " try {"
3191 " var v = obj[0];"
3192 " result = 'Wrong value ' + v + ' at iteration ' + i;"
3193 " break;"
3194 " } catch (e) {"
3195 " /* pass */"
3196 " }"
3197 "}"
3198 "result";
3199 ExpectString(code, "PASSED");
3200}
3201
3202
3203THREADED_TEST(IndexedInterceptorWithDifferentIndices) {
3204 v8::Isolate* isolate = CcTest::isolate();
3205 v8::HandleScope scope(isolate);
3206 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
3207 templ->SetHandler(
3208 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
3209
3210 LocalContext context;
3211 Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked();
3212 context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust();
3213
3214 const char* code =
3215 "try {"
3216 " for (var i = 0; i < 100; i++) {"
3217 " var v = obj[i];"
3218 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3219 " }"
3220 " 'PASSED'"
3221 "} catch(e) {"
3222 " e"
3223 "}";
3224 ExpectString(code, "PASSED");
3225}
3226
3227
3228THREADED_TEST(IndexedInterceptorWithNegativeIndices) {
3229 v8::Isolate* isolate = CcTest::isolate();
3230 v8::HandleScope scope(isolate);
3231 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
3232 templ->SetHandler(
3233 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
3234
3235 LocalContext context;
3236 Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked();
3237 context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust();
3238
3239 const char* code =
3240 "try {"
3241 " for (var i = 0; i < 100; i++) {"
3242 " var expected = i;"
3243 " var key = i;"
3244 " if (i == 25) {"
3245 " key = -1;"
3246 " expected = undefined;"
3247 " }"
3248 " if (i == 50) {"
3249 " /* probe minimal Smi number on 32-bit platforms */"
3250 " key = -(1 << 30);"
3251 " expected = undefined;"
3252 " }"
3253 " if (i == 75) {"
3254 " /* probe minimal Smi number on 64-bit platforms */"
3255 " key = 1 << 31;"
3256 " expected = undefined;"
3257 " }"
3258 " var v = obj[key];"
3259 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3260 " }"
3261 " 'PASSED'"
3262 "} catch(e) {"
3263 " e"
3264 "}";
3265 ExpectString(code, "PASSED");
3266}
3267
3268
3269THREADED_TEST(IndexedInterceptorWithNotSmiLookup) {
3270 v8::Isolate* isolate = CcTest::isolate();
3271 v8::HandleScope scope(isolate);
3272 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
3273 templ->SetHandler(
3274 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
3275
3276 LocalContext context;
3277 Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked();
3278 context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust();
3279
3280 const char* code =
3281 "try {"
3282 " for (var i = 0; i < 100; i++) {"
3283 " var expected = i;"
3284 " var key = i;"
3285 " if (i == 50) {"
3286 " key = 'foobar';"
3287 " expected = undefined;"
3288 " }"
3289 " var v = obj[key];"
3290 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3291 " }"
3292 " 'PASSED'"
3293 "} catch(e) {"
3294 " e"
3295 "}";
3296 ExpectString(code, "PASSED");
3297}
3298
3299
3300THREADED_TEST(IndexedInterceptorGoingMegamorphic) {
3301 v8::Isolate* isolate = CcTest::isolate();
3302 v8::HandleScope scope(isolate);
3303 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
3304 templ->SetHandler(
3305 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
3306
3307 LocalContext context;
3308 Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked();
3309 context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust();
3310
3311 const char* code =
3312 "var original = obj;"
3313 "try {"
3314 " for (var i = 0; i < 100; i++) {"
3315 " var expected = i;"
3316 " if (i == 50) {"
3317 " obj = {50: 'foobar'};"
3318 " expected = 'foobar';"
3319 " }"
3320 " var v = obj[i];"
3321 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3322 " if (i == 50) obj = original;"
3323 " }"
3324 " 'PASSED'"
3325 "} catch(e) {"
3326 " e"
3327 "}";
3328 ExpectString(code, "PASSED");
3329}
3330
3331
3332THREADED_TEST(IndexedInterceptorReceiverTurningSmi) {
3333 v8::Isolate* isolate = CcTest::isolate();
3334 v8::HandleScope scope(isolate);
3335 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
3336 templ->SetHandler(
3337 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
3338
3339 LocalContext context;
3340 Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked();
3341 context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust();
3342
3343 const char* code =
3344 "var original = obj;"
3345 "try {"
3346 " for (var i = 0; i < 100; i++) {"
3347 " var expected = i;"
3348 " if (i == 5) {"
3349 " obj = 239;"
3350 " expected = undefined;"
3351 " }"
3352 " var v = obj[i];"
3353 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3354 " if (i == 5) obj = original;"
3355 " }"
3356 " 'PASSED'"
3357 "} catch(e) {"
3358 " e"
3359 "}";
3360 ExpectString(code, "PASSED");
3361}
3362
3363
3364THREADED_TEST(IndexedInterceptorOnProto) {
3365 v8::Isolate* isolate = CcTest::isolate();
3366 v8::HandleScope scope(isolate);
3367 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
3368 templ->SetHandler(
3369 v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
3370
3371 LocalContext context;
3372 Local<v8::Object> obj = templ->NewInstance(context.local()).ToLocalChecked();
3373 context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust();
3374
3375 const char* code =
3376 "var o = {__proto__: obj};"
3377 "try {"
3378 " for (var i = 0; i < 100; i++) {"
3379 " var v = o[i];"
3380 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3381 " }"
3382 " 'PASSED'"
3383 "} catch(e) {"
3384 " e"
3385 "}";
3386 ExpectString(code, "PASSED");
3387}
3388
3389namespace {
3390
3391void CheckIndexedInterceptorHasIC(v8::IndexedPropertyGetterCallback getter,
3392 v8::IndexedPropertyQueryCallback query,
3393 const char* source, int expected) {
3394 v8::Isolate* isolate = CcTest::isolate();
3395 v8::HandleScope scope(isolate);
3396 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
3397 templ->SetHandler(v8::IndexedPropertyHandlerConfiguration(
3398 getter, nullptr, query, nullptr, nullptr, v8_str("data")));
3399 LocalContext context;
3400 context->Global()
3401 ->Set(context.local(), v8_str("o"),
3402 templ->NewInstance(context.local()).ToLocalChecked())
3403 .FromJust();
3404 v8::Local<Value> value = CompileRun(source);
3405 CHECK_EQ(expected, value->Int32Value(context.local()).FromJust());
3406}
3407
3408int indexed_query_counter = 0;
3409void IndexedQueryCallback(uint32_t index,
3410 const v8::PropertyCallbackInfo<v8::Integer>& info) {
3411 indexed_query_counter++;
3412}
3413
3414void IndexHasICQueryAbsent(uint32_t index,
3415 const v8::PropertyCallbackInfo<v8::Integer>& info) {
3416 ApiTestFuzzer::Fuzz();
3417 v8::Isolate* isolate = CcTest::isolate();
3418 CHECK_EQ(isolate, info.GetIsolate());
3419 info.GetReturnValue().Set(v8::Integer::New(isolate, v8::internal::ABSENT));
3420}
3421
3422} // namespace
3423
3424THREADED_TEST(IndexedInterceptorHasIC) {
3425 indexed_query_counter = 0;
3426 CheckIndexedInterceptorHasIC(nullptr, IndexedQueryCallback,
3427 "var result = 0;"
3428 "for (var i = 0; i < 1000; i++) {"
3429 " i in o;"
3430 "}",
3431 0);
3432 CHECK_EQ(1000, indexed_query_counter);
3433}
3434
3435THREADED_TEST(IndexedInterceptorHasICQueryAbsent) {
3436 CheckIndexedInterceptorHasIC(nullptr,
3437 // HasICQuery<uint32_t, v8::internal::ABSENT>,
3438 IndexHasICQueryAbsent,
3439 "var result = 0;"
3440 "for (var i = 0; i < 1000; i++) {"
3441 " if (i in o) ++result;"
3442 "}",
3443 0);
3444}
3445
3446THREADED_TEST(IndexedInterceptorHasICQueryNone) {
3447 CheckIndexedInterceptorHasIC(nullptr,
3448 HasICQuery<uint32_t, v8::internal::NONE>,
3449 "var result = 0;"
3450 "for (var i = 0; i < 1000; i++) {"
3451 " if (i in o) ++result;"
3452 "}",
3453 1000);
3454}
3455
3456THREADED_TEST(IndexedInterceptorHasICGetter) {
3457 CheckIndexedInterceptorHasIC(IdentityIndexedPropertyGetter, nullptr,
3458 "var result = 0;"
3459 "for (var i = 0; i < 1000; i++) {"
3460 " if (i in o) ++result;"
3461 "}",
3462 1000);
3463}
3464
3465THREADED_TEST(IndexedInterceptorHasICQueryGetter) {
3466 CheckIndexedInterceptorHasIC(IdentityIndexedPropertyGetter,
3467 HasICQuery<uint32_t, v8::internal::ABSENT>,
3468 "var result = 0;"
3469 "for (var i = 0; i < 1000; i++) {"
3470 " if (i in o) ++result;"
3471 "}",
3472 0);
3473}
3474
3475THREADED_TEST(IndexedInterceptorHasICQueryToggle) {
3476 CheckIndexedInterceptorHasIC(IdentityIndexedPropertyGetter,
3477 HasICQueryToggle<uint32_t>,
3478 "var result = 0;"
3479 "for (var i = 0; i < 1000; i++) {"
3480 " if (i in o) ++result;"
3481 "}",
3482 500);
3483}
3484
3485static void NoBlockGetterX(Local<Name> name,
3486 const v8::PropertyCallbackInfo<v8::Value>&) {}
3487
3488
3489static void NoBlockGetterI(uint32_t index,
3490 const v8::PropertyCallbackInfo<v8::Value>&) {}
3491
3492
3493static void PDeleter(Local<Name> name,
3494 const v8::PropertyCallbackInfo<v8::Boolean>& info) {
3495 if (!name->Equals(info.GetIsolate()->GetCurrentContext(), v8_str("foo"))
3496 .FromJust()) {
3497 return; // not intercepted
3498 }
3499
3500 info.GetReturnValue().Set(false); // intercepted, don't delete the property
3501}
3502
3503
3504static void IDeleter(uint32_t index,
3505 const v8::PropertyCallbackInfo<v8::Boolean>& info) {
3506 if (index != 2) {
3507 return; // not intercepted
3508 }
3509
3510 info.GetReturnValue().Set(false); // intercepted, don't delete the property
3511}
3512
3513
3514THREADED_TEST(Deleter) {
3515 v8::Isolate* isolate = CcTest::isolate();
3516 v8::HandleScope scope(isolate);
3517 v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate);
3518 obj->SetHandler(v8::NamedPropertyHandlerConfiguration(
3519 NoBlockGetterX, nullptr, nullptr, PDeleter, nullptr));
3520 obj->SetHandler(v8::IndexedPropertyHandlerConfiguration(
3521 NoBlockGetterI, nullptr, nullptr, IDeleter, nullptr));
3522 LocalContext context;
3523 context->Global()
3524 ->Set(context.local(), v8_str("k"),
3525 obj->NewInstance(context.local()).ToLocalChecked())
3526 .FromJust();
3527 CompileRun(
3528 "k.foo = 'foo';"
3529 "k.bar = 'bar';"
3530 "k[2] = 2;"
3531 "k[4] = 4;");
3532 CHECK(v8_compile("delete k.foo")
3533 ->Run(context.local())
3534 .ToLocalChecked()
3535 ->IsFalse());
3536 CHECK(v8_compile("delete k.bar")
3537 ->Run(context.local())
3538 .ToLocalChecked()
3539 ->IsTrue());
3540
3541 CHECK(v8_compile("k.foo")
3542 ->Run(context.local())
3543 .ToLocalChecked()
3544 ->Equals(context.local(), v8_str("foo"))
3545 .FromJust());
3546 CHECK(v8_compile("k.bar")
3547 ->Run(context.local())
3548 .ToLocalChecked()
3549 ->IsUndefined());
3550
3551 CHECK(v8_compile("delete k[2]")
3552 ->Run(context.local())
3553 .ToLocalChecked()
3554 ->IsFalse());
3555 CHECK(v8_compile("delete k[4]")
3556 ->Run(context.local())
3557 .ToLocalChecked()
3558 ->IsTrue());
3559
3560 CHECK(v8_compile("k[2]")
3561 ->Run(context.local())
3562 .ToLocalChecked()
3563 ->Equals(context.local(), v8_num(2))
3564 .FromJust());
3565 CHECK(
3566 v8_compile("k[4]")->Run(context.local()).ToLocalChecked()->IsUndefined());
3567}
3568
3569
3570static void GetK(Local<Name> name,
3571 const v8::PropertyCallbackInfo<v8::Value>& info) {
3572 ApiTestFuzzer::Fuzz();
3573 v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
3574 if (name->Equals(context, v8_str("foo")).FromJust() ||
3575 name->Equals(context, v8_str("bar")).FromJust() ||
3576 name->Equals(context, v8_str("baz")).FromJust()) {
3577 info.GetReturnValue().SetUndefined();
3578 }
3579}
3580
3581
3582static void IndexedGetK(uint32_t index,
3583 const v8::PropertyCallbackInfo<v8::Value>& info) {
3584 ApiTestFuzzer::Fuzz();
3585 if (index == 0 || index == 1) info.GetReturnValue().SetUndefined();
3586}
3587
3588
3589static void NamedEnum(const v8::PropertyCallbackInfo<v8::Array>& info) {
3590 ApiTestFuzzer::Fuzz();
3591 v8::Local<v8::Array> result = v8::Array::New(info.GetIsolate(), 3);
3592 v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
3593 CHECK(
3594 result
3595 ->Set(context, v8::Integer::New(info.GetIsolate(), 0), v8_str("foo"))
3596 .FromJust());
3597 CHECK(
3598 result
3599 ->Set(context, v8::Integer::New(info.GetIsolate(), 1), v8_str("bar"))
3600 .FromJust());
3601 CHECK(
3602 result
3603 ->Set(context, v8::Integer::New(info.GetIsolate(), 2), v8_str("baz"))
3604 .FromJust());
3605 info.GetReturnValue().Set(result);
3606}
3607
3608
3609static void IndexedEnum(const v8::PropertyCallbackInfo<v8::Array>& info) {
3610 ApiTestFuzzer::Fuzz();
3611 v8::Local<v8::Array> result = v8::Array::New(info.GetIsolate(), 2);
3612 v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
3613 CHECK(
3614 result->Set(context, v8::Integer::New(info.GetIsolate(), 0), v8_str("0"))
3615 .FromJust());
3616 CHECK(
3617 result->Set(context, v8::Integer::New(info.GetIsolate(), 1), v8_str("1"))
3618 .FromJust());
3619 info.GetReturnValue().Set(result);
3620}
3621
3622
3623THREADED_TEST(Enumerators) {
3624 v8::Isolate* isolate = CcTest::isolate();
3625 v8::HandleScope scope(isolate);
3626 v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate);
3627 obj->SetHandler(v8::NamedPropertyHandlerConfiguration(GetK, nullptr, nullptr,
3628 nullptr, NamedEnum));
3629 obj->SetHandler(v8::IndexedPropertyHandlerConfiguration(
3630 IndexedGetK, nullptr, nullptr, nullptr, IndexedEnum));
3631 LocalContext context;
3632 context->Global()
3633 ->Set(context.local(), v8_str("k"),
3634 obj->NewInstance(context.local()).ToLocalChecked())
3635 .FromJust();
3636 v8::Local<v8::Array> result =
3637 v8::Local<v8::Array>::Cast(CompileRun("k[10] = 0;"
3638 "k.a = 0;"
3639 "k[5] = 0;"
3640 "k.b = 0;"
3641 "k[4294967294] = 0;"
3642 "k.c = 0;"
3643 "k[4294967295] = 0;"
3644 "k.d = 0;"
3645 "k[140000] = 0;"
3646 "k.e = 0;"
3647 "k[30000000000] = 0;"
3648 "k.f = 0;"
3649 "var result = [];"
3650 "for (var prop in k) {"
3651 " result.push(prop);"
3652 "}"
3653 "result"));
3654 // Check that we get all the property names returned including the
3655 // ones from the enumerators in the right order: indexed properties
3656 // in numerical order, indexed interceptor properties, named
3657 // properties in insertion order, named interceptor properties.
3658 // This order is not mandated by the spec, so this test is just
3659 // documenting our behavior.
3660 CHECK_EQ(17u, result->Length());
3661 // Indexed properties.
3662 CHECK(v8_str("5")
3663 ->Equals(context.local(),
3664 result->Get(context.local(), v8::Integer::New(isolate, 0))
3665 .ToLocalChecked())
3666 .FromJust());
3667 CHECK(v8_str("10")
3668 ->Equals(context.local(),
3669 result->Get(context.local(), v8::Integer::New(isolate, 1))
3670 .ToLocalChecked())
3671 .FromJust());
3672 CHECK(v8_str("140000")
3673 ->Equals(context.local(),
3674 result->Get(context.local(), v8::Integer::New(isolate, 2))
3675 .ToLocalChecked())
3676 .FromJust());
3677 CHECK(v8_str("4294967294")
3678 ->Equals(context.local(),
3679 result->Get(context.local(), v8::Integer::New(isolate, 3))
3680 .ToLocalChecked())
3681 .FromJust());
3682 // Indexed Interceptor properties
3683 CHECK(v8_str("0")
3684 ->Equals(context.local(),
3685 result->Get(context.local(), v8::Integer::New(isolate, 4))
3686 .ToLocalChecked())
3687 .FromJust());
3688 CHECK(v8_str("1")
3689 ->Equals(context.local(),
3690 result->Get(context.local(), v8::Integer::New(isolate, 5))
3691 .ToLocalChecked())
3692 .FromJust());
3693 // Named properties in insertion order.
3694 CHECK(v8_str("a")
3695 ->Equals(context.local(),
3696 result->Get(context.local(), v8::Integer::New(isolate, 6))
3697 .ToLocalChecked())
3698 .FromJust());
3699 CHECK(v8_str("b")
3700 ->Equals(context.local(),
3701 result->Get(context.local(), v8::Integer::New(isolate, 7))
3702 .ToLocalChecked())
3703 .FromJust());
3704 CHECK(v8_str("c")
3705 ->Equals(context.local(),
3706 result->Get(context.local(), v8::Integer::New(isolate, 8))
3707 .ToLocalChecked())
3708 .FromJust());
3709 CHECK(v8_str("4294967295")
3710 ->Equals(context.local(),
3711 result->Get(context.local(), v8::Integer::New(isolate, 9))
3712 .ToLocalChecked())
3713 .FromJust());
3714 CHECK(v8_str("d")
3715 ->Equals(context.local(),
3716 result->Get(context.local(), v8::Integer::New(isolate, 10))
3717 .ToLocalChecked())
3718 .FromJust());
3719 CHECK(v8_str("e")
3720 ->Equals(context.local(),
3721 result->Get(context.local(), v8::Integer::New(isolate, 11))
3722 .ToLocalChecked())
3723 .FromJust());
3724 CHECK(v8_str("30000000000")
3725 ->Equals(context.local(),
3726 result->Get(context.local(), v8::Integer::New(isolate, 12))
3727 .ToLocalChecked())
3728 .FromJust());
3729 CHECK(v8_str("f")
3730 ->Equals(context.local(),
3731 result->Get(context.local(), v8::Integer::New(isolate, 13))
3732 .ToLocalChecked())
3733 .FromJust());
3734 // Named interceptor properties.
3735 CHECK(v8_str("foo")
3736 ->Equals(context.local(),
3737 result->Get(context.local(), v8::Integer::New(isolate, 14))
3738 .ToLocalChecked())
3739 .FromJust());
3740 CHECK(v8_str("bar")
3741 ->Equals(context.local(),
3742 result->Get(context.local(), v8::Integer::New(isolate, 15))
3743 .ToLocalChecked())
3744 .FromJust());
3745 CHECK(v8_str("baz")
3746 ->Equals(context.local(),
3747 result->Get(context.local(), v8::Integer::New(isolate, 16))
3748 .ToLocalChecked())
3749 .FromJust());
3750}
3751
3752
3753v8::Local<Value> call_ic_function;
3754v8::Local<Value> call_ic_function2;
3755v8::Local<Value> call_ic_function3;
3756
3757static void InterceptorCallICGetter(
3758 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
3759 ApiTestFuzzer::Fuzz();
3760 CHECK(v8_str("x")
3761 ->Equals(info.GetIsolate()->GetCurrentContext(), name)
3762 .FromJust());
3763 info.GetReturnValue().Set(call_ic_function);
3764}
3765
3766
3767// This test should hit the call IC for the interceptor case.
3768THREADED_TEST(InterceptorCallIC) {
3769 v8::Isolate* isolate = CcTest::isolate();
3770 v8::HandleScope scope(isolate);
3771 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
3772 templ->SetHandler(
3773 v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter));
3774 LocalContext context;
3775 context->Global()
3776 ->Set(context.local(), v8_str("o"),
3777 templ->NewInstance(context.local()).ToLocalChecked())
3778 .FromJust();
3779 call_ic_function = v8_compile("function f(x) { return x + 1; }; f")
3780 ->Run(context.local())
3781 .ToLocalChecked();
3782 v8::Local<Value> value = CompileRun(
3783 "var result = 0;"
3784 "for (var i = 0; i < 1000; i++) {"
3785 " result = o.x(41);"
3786 "}");
3787 CHECK_EQ(42, value->Int32Value(context.local()).FromJust());
3788}
3789
3790
3791// This test checks that if interceptor doesn't provide
3792// a value, we can fetch regular value.
3793THREADED_TEST(InterceptorCallICSeesOthers) {
3794 v8::Isolate* isolate = CcTest::isolate();
3795 v8::HandleScope scope(isolate);
3796 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
3797 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
3798 LocalContext context;
3799 context->Global()
3800 ->Set(context.local(), v8_str("o"),
3801 templ->NewInstance(context.local()).ToLocalChecked())
3802 .FromJust();
3803 v8::Local<Value> value = CompileRun(
3804 "o.x = function f(x) { return x + 1; };"
3805 "var result = 0;"
3806 "for (var i = 0; i < 7; i++) {"
3807 " result = o.x(41);"
3808 "}");
3809 CHECK_EQ(42, value->Int32Value(context.local()).FromJust());
3810}
3811
3812
3813static v8::Local<Value> call_ic_function4;
3814static void InterceptorCallICGetter4(
3815 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
3816 ApiTestFuzzer::Fuzz();
3817 CHECK(v8_str("x")
3818 ->Equals(info.GetIsolate()->GetCurrentContext(), name)
3819 .FromJust());
3820 info.GetReturnValue().Set(call_ic_function4);
3821}
3822
3823
3824// This test checks that if interceptor provides a function,
3825// even if we cached shadowed variant, interceptor's function
3826// is invoked
3827THREADED_TEST(InterceptorCallICCacheableNotNeeded) {
3828 v8::Isolate* isolate = CcTest::isolate();
3829 v8::HandleScope scope(isolate);
3830 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
3831 templ->SetHandler(
3832 v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter4));
3833 LocalContext context;
3834 context->Global()
3835 ->Set(context.local(), v8_str("o"),
3836 templ->NewInstance(context.local()).ToLocalChecked())
3837 .FromJust();
3838 call_ic_function4 = v8_compile("function f(x) { return x - 1; }; f")
3839 ->Run(context.local())
3840 .ToLocalChecked();
3841 v8::Local<Value> value = CompileRun(
3842 "Object.getPrototypeOf(o).x = function(x) { return x + 1; };"
3843 "var result = 0;"
3844 "for (var i = 0; i < 1000; i++) {"
3845 " result = o.x(42);"
3846 "}");
3847 CHECK_EQ(41, value->Int32Value(context.local()).FromJust());
3848}
3849
3850
3851// Test the case when we stored cacheable lookup into
3852// a stub, but it got invalidated later on
3853THREADED_TEST(InterceptorCallICInvalidatedCacheable) {
3854 v8::Isolate* isolate = CcTest::isolate();
3855 v8::HandleScope scope(isolate);
3856 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
3857 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
3858 LocalContext context;
3859 context->Global()
3860 ->Set(context.local(), v8_str("o"),
3861 templ->NewInstance(context.local()).ToLocalChecked())
3862 .FromJust();
3863 v8::Local<Value> value = CompileRun(
3864 "proto1 = new Object();"
3865 "proto2 = new Object();"
3866 "o.__proto__ = proto1;"
3867 "proto1.__proto__ = proto2;"
3868 "proto2.y = function(x) { return x + 1; };"
3869 // Invoke it many times to compile a stub
3870 "for (var i = 0; i < 7; i++) {"
3871 " o.y(42);"
3872 "}"
3873 "proto1.y = function(x) { return x - 1; };"
3874 "var result = 0;"
3875 "for (var i = 0; i < 7; i++) {"
3876 " result += o.y(42);"
3877 "}");
3878 CHECK_EQ(41 * 7, value->Int32Value(context.local()).FromJust());
3879}
3880
3881
3882// This test checks that if interceptor doesn't provide a function,
3883// cached constant function is used
3884THREADED_TEST(InterceptorCallICConstantFunctionUsed) {
3885 v8::Isolate* isolate = CcTest::isolate();
3886 v8::HandleScope scope(isolate);
3887 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
3888 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
3889 LocalContext context;
3890 context->Global()
3891 ->Set(context.local(), v8_str("o"),
3892 templ->NewInstance(context.local()).ToLocalChecked())
3893 .FromJust();
3894 v8::Local<Value> value = CompileRun(
3895 "function inc(x) { return x + 1; };"
3896 "inc(1);"
3897 "o.x = inc;"
3898 "var result = 0;"
3899 "for (var i = 0; i < 1000; i++) {"
3900 " result = o.x(42);"
3901 "}");
3902 CHECK_EQ(43, value->Int32Value(context.local()).FromJust());
3903}
3904
3905
3906static v8::Local<Value> call_ic_function5;
3907static void InterceptorCallICGetter5(
3908 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
3909 ApiTestFuzzer::Fuzz();
3910 if (v8_str("x")
3911 ->Equals(info.GetIsolate()->GetCurrentContext(), name)
3912 .FromJust())
3913 info.GetReturnValue().Set(call_ic_function5);
3914}
3915
3916
3917// This test checks that if interceptor provides a function,
3918// even if we cached constant function, interceptor's function
3919// is invoked
3920THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) {
3921 v8::Isolate* isolate = CcTest::isolate();
3922 v8::HandleScope scope(isolate);
3923 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
3924 templ->SetHandler(
3925 v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter5));
3926 LocalContext context;
3927 context->Global()
3928 ->Set(context.local(), v8_str("o"),
3929 templ->NewInstance(context.local()).ToLocalChecked())
3930 .FromJust();
3931 call_ic_function5 = v8_compile("function f(x) { return x - 1; }; f")
3932 ->Run(context.local())
3933 .ToLocalChecked();
3934 v8::Local<Value> value = CompileRun(
3935 "function inc(x) { return x + 1; };"
3936 "inc(1);"
3937 "o.x = inc;"
3938 "var result = 0;"
3939 "for (var i = 0; i < 1000; i++) {"
3940 " result = o.x(42);"
3941 "}");
3942 CHECK_EQ(41, value->Int32Value(context.local()).FromJust());
3943}
3944
3945
3946static v8::Local<Value> call_ic_function6;
3947static void InterceptorCallICGetter6(
3948 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
3949 ApiTestFuzzer::Fuzz();
3950 if (v8_str("x")
3951 ->Equals(info.GetIsolate()->GetCurrentContext(), name)
3952 .FromJust())
3953 info.GetReturnValue().Set(call_ic_function6);
3954}
3955
3956
3957// Same test as above, except the code is wrapped in a function
3958// to test the optimized compiler.
3959THREADED_TEST(InterceptorCallICConstantFunctionNotNeededWrapped) {
3960 i::FLAG_allow_natives_syntax = true;
3961 v8::Isolate* isolate = CcTest::isolate();
3962 v8::HandleScope scope(isolate);
3963 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
3964 templ->SetHandler(
3965 v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter6));
3966 LocalContext context;
3967 context->Global()
3968 ->Set(context.local(), v8_str("o"),
3969 templ->NewInstance(context.local()).ToLocalChecked())
3970 .FromJust();
3971 call_ic_function6 = v8_compile("function f(x) { return x - 1; }; f")
3972 ->Run(context.local())
3973 .ToLocalChecked();
3974 v8::Local<Value> value = CompileRun(
3975 "function inc(x) { return x + 1; };"
3976 "inc(1);"
3977 "o.x = inc;"
3978 "function test() {"
3979 " var result = 0;"
3980 " for (var i = 0; i < 1000; i++) {"
3981 " result = o.x(42);"
3982 " }"
3983 " return result;"
3984 "};"
3985 "%PrepareFunctionForOptimization(test);"
3986 "test();"
3987 "test();"
3988 "test();"
3989 "%OptimizeFunctionOnNextCall(test);"
3990 "test()");
3991 CHECK_EQ(41, value->Int32Value(context.local()).FromJust());
3992}
3993
3994
3995// Test the case when we stored constant function into
3996// a stub, but it got invalidated later on
3997THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) {
3998 v8::Isolate* isolate = CcTest::isolate();
3999 v8::HandleScope scope(isolate);
4000 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
4001 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
4002 LocalContext context;
4003 context->Global()
4004 ->Set(context.local(), v8_str("o"),
4005 templ->NewInstance(context.local()).ToLocalChecked())
4006 .FromJust();
4007 v8::Local<Value> value = CompileRun(
4008 "function inc(x) { return x + 1; };"
4009 "inc(1);"
4010 "proto1 = new Object();"
4011 "proto2 = new Object();"
4012 "o.__proto__ = proto1;"
4013 "proto1.__proto__ = proto2;"
4014 "proto2.y = inc;"
4015 // Invoke it many times to compile a stub
4016 "for (var i = 0; i < 7; i++) {"
4017 " o.y(42);"
4018 "}"
4019 "proto1.y = function(x) { return x - 1; };"
4020 "var result = 0;"
4021 "for (var i = 0; i < 7; i++) {"
4022 " result += o.y(42);"
4023 "}");
4024 CHECK_EQ(41 * 7, value->Int32Value(context.local()).FromJust());
4025}
4026
4027
4028// Test the case when we stored constant function into
4029// a stub, but it got invalidated later on due to override on
4030// global object which is between interceptor and constant function' holders.
4031THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) {
4032 v8::Isolate* isolate = CcTest::isolate();
4033 v8::HandleScope scope(isolate);
4034 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
4035 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
4036 LocalContext context;
4037 context->Global()
4038 ->Set(context.local(), v8_str("o"),
4039 templ->NewInstance(context.local()).ToLocalChecked())
4040 .FromJust();
4041 v8::Local<Value> value = CompileRun(
4042 "function inc(x) { return x + 1; };"
4043 "inc(1);"
4044 "o.__proto__ = this;"
4045 "this.__proto__.y = inc;"
4046 // Invoke it many times to compile a stub
4047 "for (var i = 0; i < 7; i++) {"
4048 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);"
4049 "}"
4050 "this.y = function(x) { return x - 1; };"
4051 "var result = 0;"
4052 "for (var i = 0; i < 7; i++) {"
4053 " result += o.y(42);"
4054 "}");
4055 CHECK_EQ(41 * 7, value->Int32Value(context.local()).FromJust());
4056}
4057
4058
4059// Test the case when actual function to call sits on global object.
4060THREADED_TEST(InterceptorCallICCachedFromGlobal) {
4061 v8::Isolate* isolate = CcTest::isolate();
4062 v8::HandleScope scope(isolate);
4063 v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
4064 templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
4065
4066 LocalContext context;
4067 context->Global()
4068 ->Set(context.local(), v8_str("o"),
4069 templ_o->NewInstance(context.local()).ToLocalChecked())
4070 .FromJust();
4071
4072 v8::Local<Value> value = CompileRun(
4073 "try {"
4074 " o.__proto__ = this;"
4075 " for (var i = 0; i < 10; i++) {"
4076 " var v = o.parseFloat('239');"
4077 " if (v != 239) throw v;"
4078 // Now it should be ICed and keep a reference to parseFloat.
4079 " }"
4080 " var result = 0;"
4081 " for (var i = 0; i < 10; i++) {"
4082 " result += o.parseFloat('239');"
4083 " }"
4084 " result"
4085 "} catch(e) {"
4086 " e"
4087 "};");
4088 CHECK_EQ(239 * 10, value->Int32Value(context.local()).FromJust());
4089}
4090
4091
4092v8::Local<Value> keyed_call_ic_function;
4093
4094static void InterceptorKeyedCallICGetter(
4095 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
4096 ApiTestFuzzer::Fuzz();
4097 if (v8_str("x")
4098 ->Equals(info.GetIsolate()->GetCurrentContext(), name)
4099 .FromJust()) {
4100 info.GetReturnValue().Set(keyed_call_ic_function);
4101 }
4102}
4103
4104
4105// Test the case when we stored cacheable lookup into
4106// a stub, but the function name changed (to another cacheable function).
4107THREADED_TEST(InterceptorKeyedCallICKeyChange1) {
4108 v8::Isolate* isolate = CcTest::isolate();
4109 v8::HandleScope scope(isolate);
4110 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
4111 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
4112 LocalContext context;
4113 context->Global()
4114 ->Set(context.local(), v8_str("o"),
4115 templ->NewInstance(context.local()).ToLocalChecked())
4116 .FromJust();
4117 CompileRun(
4118 "proto = new Object();"
4119 "proto.y = function(x) { return x + 1; };"
4120 "proto.z = function(x) { return x - 1; };"
4121 "o.__proto__ = proto;"
4122 "var result = 0;"
4123 "var method = 'y';"
4124 "for (var i = 0; i < 10; i++) {"
4125 " if (i == 5) { method = 'z'; };"
4126 " result += o[method](41);"
4127 "}");
4128 CHECK_EQ(42 * 5 + 40 * 5, context->Global()
4129 ->Get(context.local(), v8_str("result"))
4130 .ToLocalChecked()
4131 ->Int32Value(context.local())
4132 .FromJust());
4133}
4134
4135
4136// Test the case when we stored cacheable lookup into
4137// a stub, but the function name changed (and the new function is present
4138// both before and after the interceptor in the prototype chain).
4139THREADED_TEST(InterceptorKeyedCallICKeyChange2) {
4140 v8::Isolate* isolate = CcTest::isolate();
4141 v8::HandleScope scope(isolate);
4142 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
4143 templ->SetHandler(
4144 v8::NamedPropertyHandlerConfiguration(InterceptorKeyedCallICGetter));
4145 LocalContext context;
4146 context->Global()
4147 ->Set(context.local(), v8_str("proto1"),
4148 templ->NewInstance(context.local()).ToLocalChecked())
4149 .FromJust();
4150 keyed_call_ic_function = v8_compile("function f(x) { return x - 1; }; f")
4151 ->Run(context.local())
4152 .ToLocalChecked();
4153 CompileRun(
4154 "o = new Object();"
4155 "proto2 = new Object();"
4156 "o.y = function(x) { return x + 1; };"
4157 "proto2.y = function(x) { return x + 2; };"
4158 "o.__proto__ = proto1;"
4159 "proto1.__proto__ = proto2;"
4160 "var result = 0;"
4161 "var method = 'x';"
4162 "for (var i = 0; i < 10; i++) {"
4163 " if (i == 5) { method = 'y'; };"
4164 " result += o[method](41);"
4165 "}");
4166 CHECK_EQ(42 * 5 + 40 * 5, context->Global()
4167 ->Get(context.local(), v8_str("result"))
4168 .ToLocalChecked()
4169 ->Int32Value(context.local())
4170 .FromJust());
4171}
4172
4173
4174// Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit
4175// on the global object.
4176THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) {
4177 v8::Isolate* isolate = CcTest::isolate();
4178 v8::HandleScope scope(isolate);
4179 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
4180 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
4181 LocalContext context;
4182 context->Global()
4183 ->Set(context.local(), v8_str("o"),
4184 templ->NewInstance(context.local()).ToLocalChecked())
4185 .FromJust();
4186 CompileRun(
4187 "function inc(x) { return x + 1; };"
4188 "inc(1);"
4189 "function dec(x) { return x - 1; };"
4190 "dec(1);"
4191 "o.__proto__ = this;"
4192 "this.__proto__.x = inc;"
4193 "this.__proto__.y = dec;"
4194 "var result = 0;"
4195 "var method = 'x';"
4196 "for (var i = 0; i < 10; i++) {"
4197 " if (i == 5) { method = 'y'; };"
4198 " result += o[method](41);"
4199 "}");
4200 CHECK_EQ(42 * 5 + 40 * 5, context->Global()
4201 ->Get(context.local(), v8_str("result"))
4202 .ToLocalChecked()
4203 ->Int32Value(context.local())
4204 .FromJust());
4205}
4206
4207
4208// Test the case when actual function to call sits on global object.
4209THREADED_TEST(InterceptorKeyedCallICFromGlobal) {
4210 v8::Isolate* isolate = CcTest::isolate();
4211 v8::HandleScope scope(isolate);
4212 v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
4213 templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
4214 LocalContext context;
4215 context->Global()
4216 ->Set(context.local(), v8_str("o"),
4217 templ_o->NewInstance(context.local()).ToLocalChecked())
4218 .FromJust();
4219
4220 CompileRun(
4221 "function len(x) { return x.length; };"
4222 "o.__proto__ = this;"
4223 "var m = 'parseFloat';"
4224 "var result = 0;"
4225 "for (var i = 0; i < 10; i++) {"
4226 " if (i == 5) {"
4227 " m = 'len';"
4228 " saved_result = result;"
4229 " };"
4230 " result = o[m]('239');"
4231 "}");
4232 CHECK_EQ(3, context->Global()
4233 ->Get(context.local(), v8_str("result"))
4234 .ToLocalChecked()
4235 ->Int32Value(context.local())
4236 .FromJust());
4237 CHECK_EQ(239, context->Global()
4238 ->Get(context.local(), v8_str("saved_result"))
4239 .ToLocalChecked()
4240 ->Int32Value(context.local())
4241 .FromJust());
4242}
4243
4244
4245// Test the map transition before the interceptor.
4246THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) {
4247 v8::Isolate* isolate = CcTest::isolate();
4248 v8::HandleScope scope(isolate);
4249 v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
4250 templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
4251 LocalContext context;
4252 context->Global()
4253 ->Set(context.local(), v8_str("proto"),
4254 templ_o->NewInstance(context.local()).ToLocalChecked())
4255 .FromJust();
4256
4257 CompileRun(
4258 "var o = new Object();"
4259 "o.__proto__ = proto;"
4260 "o.method = function(x) { return x + 1; };"
4261 "var m = 'method';"
4262 "var result = 0;"
4263 "for (var i = 0; i < 10; i++) {"
4264 " if (i == 5) { o.method = function(x) { return x - 1; }; };"
4265 " result += o[m](41);"
4266 "}");
4267 CHECK_EQ(42 * 5 + 40 * 5, context->Global()
4268 ->Get(context.local(), v8_str("result"))
4269 .ToLocalChecked()
4270 ->Int32Value(context.local())
4271 .FromJust());
4272}
4273
4274
4275// Test the map transition after the interceptor.
4276THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) {
4277 v8::Isolate* isolate = CcTest::isolate();
4278 v8::HandleScope scope(isolate);
4279 v8::Local<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
4280 templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
4281 LocalContext context;
4282 context->Global()
4283 ->Set(context.local(), v8_str("o"),
4284 templ_o->NewInstance(context.local()).ToLocalChecked())
4285 .FromJust();
4286
4287 CompileRun(
4288 "var proto = new Object();"
4289 "o.__proto__ = proto;"
4290 "proto.method = function(x) { return x + 1; };"
4291 "var m = 'method';"
4292 "var result = 0;"
4293 "for (var i = 0; i < 10; i++) {"
4294 " if (i == 5) { proto.method = function(x) { return x - 1; }; };"
4295 " result += o[m](41);"
4296 "}");
4297 CHECK_EQ(42 * 5 + 40 * 5, context->Global()
4298 ->Get(context.local(), v8_str("result"))
4299 .ToLocalChecked()
4300 ->Int32Value(context.local())
4301 .FromJust());
4302}
4303
4304
4305static int interceptor_call_count = 0;
4306
4307static void InterceptorICRefErrorGetter(
4308 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
4309 ApiTestFuzzer::Fuzz();
4310 if (!is_bootstrapping &&
4311 v8_str("x")
4312 ->Equals(info.GetIsolate()->GetCurrentContext(), name)
4313 .FromJust() &&
4314 interceptor_call_count++ < 20) {
4315 info.GetReturnValue().Set(call_ic_function2);
4316 }
4317}
4318
4319
4320// This test should hit load and call ICs for the interceptor case.
4321// Once in a while, the interceptor will reply that a property was not
4322// found in which case we should get a reference error.
4323THREADED_TEST(InterceptorICReferenceErrors) {
4324 v8::Isolate* isolate = CcTest::isolate();
4325 v8::HandleScope scope(isolate);
4326 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
4327 templ->SetHandler(
4328 v8::NamedPropertyHandlerConfiguration(InterceptorICRefErrorGetter));
4329 is_bootstrapping = true;
4330 LocalContext context(nullptr, templ, v8::Local<Value>());
4331 is_bootstrapping = false;
4332 call_ic_function2 = v8_compile("function h(x) { return x; }; h")
4333 ->Run(context.local())
4334 .ToLocalChecked();
4335 v8::Local<Value> value = CompileRun(
4336 "function f() {"
4337 " for (var i = 0; i < 1000; i++) {"
4338 " try { x; } catch(e) { return true; }"
4339 " }"
4340 " return false;"
4341 "};"
4342 "f();");
4343 CHECK(value->BooleanValue(isolate));
4344 interceptor_call_count = 0;
4345 value = CompileRun(
4346 "function g() {"
4347 " for (var i = 0; i < 1000; i++) {"
4348 " try { x(42); } catch(e) { return true; }"
4349 " }"
4350 " return false;"
4351 "};"
4352 "g();");
4353 CHECK(value->BooleanValue(isolate));
4354}
4355
4356
4357static int interceptor_ic_exception_get_count = 0;
4358
4359static void InterceptorICExceptionGetter(
4360 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
4361 ApiTestFuzzer::Fuzz();
4362 if (is_bootstrapping) return;
4363 if (v8_str("x")
4364 ->Equals(info.GetIsolate()->GetCurrentContext(), name)
4365 .FromJust() &&
4366 ++interceptor_ic_exception_get_count < 20) {
4367 info.GetReturnValue().Set(call_ic_function3);
4368 }
4369 if (interceptor_ic_exception_get_count == 20) {
4370 info.GetIsolate()->ThrowException(v8_num(42));
4371 return;
4372 }
4373}
4374
4375
4376// Test interceptor load/call IC where the interceptor throws an
4377// exception once in a while.
4378THREADED_TEST(InterceptorICGetterExceptions) {
4379 interceptor_ic_exception_get_count = 0;
4380 v8::Isolate* isolate = CcTest::isolate();
4381 v8::HandleScope scope(isolate);
4382 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
4383 templ->SetHandler(
4384 v8::NamedPropertyHandlerConfiguration(InterceptorICExceptionGetter));
4385 is_bootstrapping = true;
4386 LocalContext context(nullptr, templ, v8::Local<Value>());
4387 is_bootstrapping = false;
4388 call_ic_function3 = v8_compile("function h(x) { return x; }; h")
4389 ->Run(context.local())
4390 .ToLocalChecked();
4391 v8::Local<Value> value = CompileRun(
4392 "function f() {"
4393 " for (var i = 0; i < 100; i++) {"
4394 " try { x; } catch(e) { return true; }"
4395 " }"
4396 " return false;"
4397 "};"
4398 "f();");
4399 CHECK(value->BooleanValue(isolate));
4400 interceptor_ic_exception_get_count = 0;
4401 value = CompileRun(
4402 "function f() {"
4403 " for (var i = 0; i < 100; i++) {"
4404 " try { x(42); } catch(e) { return true; }"
4405 " }"
4406 " return false;"
4407 "};"
4408 "f();");
4409 CHECK(value->BooleanValue(isolate));
4410}
4411
4412
4413static int interceptor_ic_exception_set_count = 0;
4414
4415static void InterceptorICExceptionSetter(
4416 Local<Name> key, Local<Value> value,
4417 const v8::PropertyCallbackInfo<v8::Value>& info) {
4418 ApiTestFuzzer::Fuzz();
4419 if (++interceptor_ic_exception_set_count > 20) {
4420 info.GetIsolate()->ThrowException(v8_num(42));
4421 }
4422}
4423
4424
4425// Test interceptor store IC where the interceptor throws an exception
4426// once in a while.
4427THREADED_TEST(InterceptorICSetterExceptions) {
4428 interceptor_ic_exception_set_count = 0;
4429 v8::Isolate* isolate = CcTest::isolate();
4430 v8::HandleScope scope(isolate);
4431 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
4432 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
4433 nullptr, InterceptorICExceptionSetter));
4434 LocalContext context(nullptr, templ, v8::Local<Value>());
4435 v8::Local<Value> value = CompileRun(
4436 "function f() {"
4437 " for (var i = 0; i < 100; i++) {"
4438 " try { x = 42; } catch(e) { return true; }"
4439 " }"
4440 " return false;"
4441 "};"
4442 "f();");
4443 CHECK(value->BooleanValue(isolate));
4444}
4445
4446
4447// Test that we ignore null interceptors.
4448THREADED_TEST(NullNamedInterceptor) {
4449 v8::Isolate* isolate = CcTest::isolate();
4450 v8::HandleScope scope(isolate);
4451 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
4452 templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
4453 static_cast<v8::GenericNamedPropertyGetterCallback>(nullptr)));
4454 LocalContext context;
4455 templ->Set(CcTest::isolate(), "x", v8_num(42));
4456 v8::Local<v8::Object> obj =
4457 templ->NewInstance(context.local()).ToLocalChecked();
4458 context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust();
4459 v8::Local<Value> value = CompileRun("obj.x");
4460 CHECK(value->IsInt32());
4461 CHECK_EQ(42, value->Int32Value(context.local()).FromJust());
4462}
4463
4464
4465// Test that we ignore null interceptors.
4466THREADED_TEST(NullIndexedInterceptor) {
4467 v8::Isolate* isolate = CcTest::isolate();
4468 v8::HandleScope scope(isolate);
4469 v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
4470 templ->SetHandler(v8::IndexedPropertyHandlerConfiguration(
4471 static_cast<v8::IndexedPropertyGetterCallback>(nullptr)));
4472 LocalContext context;
4473 templ->Set(CcTest::isolate(), "42", v8_num(42));
4474 v8::Local<v8::Object> obj =
4475 templ->NewInstance(context.local()).ToLocalChecked();
4476 context->Global()->Set(context.local(), v8_str("obj"), obj).FromJust();
4477 v8::Local<Value> value = CompileRun("obj[42]");
4478 CHECK(value->IsInt32());
4479 CHECK_EQ(42, value->Int32Value(context.local()).FromJust());
4480}
4481
4482
4483THREADED_TEST(NamedPropertyHandlerGetterAttributes) {
4484 v8::Isolate* isolate = CcTest::isolate();
4485 v8::HandleScope scope(isolate);
4486 v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
4487 templ->InstanceTemplate()->SetHandler(
4488 v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
4489 LocalContext env;
4490 env->Global()
4491 ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local())
4492 .ToLocalChecked()
4493 ->NewInstance(env.local())
4494 .ToLocalChecked())
4495 .FromJust();
4496 ExpectTrue("obj.x === 42");
4497 ExpectTrue("!obj.propertyIsEnumerable('x')");
4498}
4499
4500
4501THREADED_TEST(Regress256330) {
4502 if (!i::FLAG_opt) return;
4503 i::FLAG_allow_natives_syntax = true;
4504 LocalContext context;
4505 v8::HandleScope scope(context->GetIsolate());
4506 Local<FunctionTemplate> templ = FunctionTemplate::New(context->GetIsolate());
4507 AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
4508 context->Global()
4509 ->Set(context.local(), v8_str("Bug"),
4510 templ->GetFunction(context.local()).ToLocalChecked())
4511 .FromJust();
4512 CompileRun(
4513 "\"use strict\"; var o = new Bug;"
4514 "function f(o) { o.x = 10; };"
4515 "%PrepareFunctionForOptimization(f);"
4516 "f(o); f(o); f(o);"
4517 "%OptimizeFunctionOnNextCall(f);"
4518 "f(o);");
4519 int status = v8_run_int32value(v8_compile("%GetOptimizationStatus(f)"));
4520 int mask = static_cast<int>(i::OptimizationStatus::kIsFunction) |
4521 static_cast<int>(i::OptimizationStatus::kOptimized);
4522 CHECK_EQ(mask, status & mask);
4523}
4524
4525THREADED_TEST(OptimizedInterceptorSetter) {
4526 i::FLAG_allow_natives_syntax = true;
4527 v8::HandleScope scope(CcTest::isolate());
4528 Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
4529 AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
4530 LocalContext env;
4531 env->Global()
4532 ->Set(env.local(), v8_str("Obj"),
4533 templ->GetFunction(env.local()).ToLocalChecked())
4534 .FromJust();
4535 CompileRun(
4536 "var obj = new Obj;"
4537 // Initialize fields to avoid transitions later.
4538 "obj.age = 0;"
4539 "obj.accessor_age = 42;"
4540 "function setter(i) { this.accessor_age = i; };"
4541 "function getter() { return this.accessor_age; };"
4542 "function setAge(i) { obj.age = i; };"
4543 "Object.defineProperty(obj, 'age', { get:getter, set:setter });"
4544 "%PrepareFunctionForOptimization(setAge);"
4545 "setAge(1);"
4546 "setAge(2);"
4547 "setAge(3);"
4548 "%OptimizeFunctionOnNextCall(setAge);"
4549 "setAge(4);");
4550 // All stores went through the interceptor.
4551 ExpectInt32("obj.interceptor_age", 4);
4552 ExpectInt32("obj.accessor_age", 42);
4553}
4554
4555THREADED_TEST(OptimizedInterceptorGetter) {
4556 i::FLAG_allow_natives_syntax = true;
4557 v8::HandleScope scope(CcTest::isolate());
4558 Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
4559 AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
4560 LocalContext env;
4561 env->Global()
4562 ->Set(env.local(), v8_str("Obj"),
4563 templ->GetFunction(env.local()).ToLocalChecked())
4564 .FromJust();
4565 CompileRun(
4566 "var obj = new Obj;"
4567 // Initialize fields to avoid transitions later.
4568 "obj.age = 1;"
4569 "obj.accessor_age = 42;"
4570 "function getter() { return this.accessor_age; };"
4571 "function getAge() { return obj.interceptor_age; };"
4572 "Object.defineProperty(obj, 'interceptor_age', { get:getter });"
4573 "%PrepareFunctionForOptimization(getAge);"
4574 "getAge();"
4575 "getAge();"
4576 "getAge();"
4577 "%OptimizeFunctionOnNextCall(getAge);");
4578 // Access through interceptor.
4579 ExpectInt32("getAge()", 1);
4580}
4581
4582THREADED_TEST(OptimizedInterceptorFieldRead) {
4583 i::FLAG_allow_natives_syntax = true;
4584 v8::HandleScope scope(CcTest::isolate());
4585 Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
4586 AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
4587 LocalContext env;
4588 env->Global()
4589 ->Set(env.local(), v8_str("Obj"),
4590 templ->GetFunction(env.local()).ToLocalChecked())
4591 .FromJust();
4592 CompileRun(
4593 "var obj = new Obj;"
4594 "obj.__proto__.interceptor_age = 42;"
4595 "obj.age = 100;"
4596 "function getAge() { return obj.interceptor_age; };"
4597 "%PrepareFunctionForOptimization(getAge);");
4598 ExpectInt32("getAge();", 100);
4599 ExpectInt32("getAge();", 100);
4600 ExpectInt32("getAge();", 100);
4601 CompileRun("%OptimizeFunctionOnNextCall(getAge);");
4602 // Access through interceptor.
4603 ExpectInt32("getAge();", 100);
4604}
4605
4606THREADED_TEST(OptimizedInterceptorFieldWrite) {
4607 i::FLAG_allow_natives_syntax = true;
4608 v8::HandleScope scope(CcTest::isolate());
4609 Local<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate());
4610 AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
4611 LocalContext env;
4612 env->Global()
4613 ->Set(env.local(), v8_str("Obj"),
4614 templ->GetFunction(env.local()).ToLocalChecked())
4615 .FromJust();
4616 CompileRun(
4617 "var obj = new Obj;"
4618 "obj.age = 100000;"
4619 "function setAge(i) { obj.age = i };"
4620 "%PrepareFunctionForOptimization(setAge);"
4621 "setAge(100);"
4622 "setAge(101);"
4623 "setAge(102);"
4624 "%OptimizeFunctionOnNextCall(setAge);"
4625 "setAge(103);");
4626 ExpectInt32("obj.age", 100000);
4627 ExpectInt32("obj.interceptor_age", 103);
4628}
4629
4630
4631THREADED_TEST(Regress149912) {
4632 LocalContext context;
4633 v8::HandleScope scope(context->GetIsolate());
4634 Local<FunctionTemplate> templ = FunctionTemplate::New(context->GetIsolate());
4635 AddInterceptor(templ, EmptyInterceptorGetter, EmptyInterceptorSetter);
4636 context->Global()
4637 ->Set(context.local(), v8_str("Bug"),
4638 templ->GetFunction(context.local()).ToLocalChecked())
4639 .FromJust();
4640 CompileRun("Number.prototype.__proto__ = new Bug; var x = 0; x.foo();");
4641}
4642
4643THREADED_TEST(Regress625155) {
4644 LocalContext context;
4645 v8::HandleScope scope(context->GetIsolate());
4646 Local<FunctionTemplate> templ = FunctionTemplate::New(context->GetIsolate());
4647 AddInterceptor(templ, EmptyInterceptorGetter, EmptyInterceptorSetter);
4648 context->Global()
4649 ->Set(context.local(), v8_str("Bug"),
4650 templ->GetFunction(context.local()).ToLocalChecked())
4651 .FromJust();
4652 CompileRun(
4653 "Number.prototype.__proto__ = new Bug;"
4654 "var x;"
4655 "x = 0xDEAD;"
4656 "x.boom = 0;"
4657 "x = 's';"
4658 "x.boom = 0;"
4659 "x = 1.5;"
4660 "x.boom = 0;");
4661}
4662
4663THREADED_TEST(Regress125988) {
4664 v8::HandleScope scope(CcTest::isolate());
4665 Local<FunctionTemplate> intercept = FunctionTemplate::New(CcTest::isolate());
4666 AddInterceptor(intercept, EmptyInterceptorGetter, EmptyInterceptorSetter);
4667 LocalContext env;
4668 env->Global()
4669 ->Set(env.local(), v8_str("Intercept"),
4670 intercept->GetFunction(env.local()).ToLocalChecked())
4671 .FromJust();
4672 CompileRun(
4673 "var a = new Object();"
4674 "var b = new Intercept();"
4675 "var c = new Object();"
4676 "c.__proto__ = b;"
4677 "b.__proto__ = a;"
4678 "a.x = 23;"
4679 "for (var i = 0; i < 3; i++) c.x;");
4680 ExpectBoolean("c.hasOwnProperty('x')", false);
4681 ExpectInt32("c.x", 23);
4682 CompileRun(
4683 "a.y = 42;"
4684 "for (var i = 0; i < 3; i++) c.x;");
4685 ExpectBoolean("c.hasOwnProperty('x')", false);
4686 ExpectInt32("c.x", 23);
4687 ExpectBoolean("c.hasOwnProperty('y')", false);
4688 ExpectInt32("c.y", 42);
4689}
4690
4691
4692static void IndexedPropertyEnumerator(
4693 const v8::PropertyCallbackInfo<v8::Array>& info) {
4694 v8::Local<v8::Array> result = v8::Array::New(info.GetIsolate(), 1);
4695 result->Set(info.GetIsolate()->GetCurrentContext(), 0,
4696 v8::Integer::New(info.GetIsolate(), 7))
4697 .FromJust();
4698 info.GetReturnValue().Set(result);
4699}
4700
4701
4702static void NamedPropertyEnumerator(
4703 const v8::PropertyCallbackInfo<v8::Array>& info) {
4704 v8::Local<v8::Array> result = v8::Array::New(info.GetIsolate(), 2);
4705 v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
4706 result->Set(context, 0, v8_str("x")).FromJust();
4707 result->Set(context, 1, v8::Symbol::GetIterator(info.GetIsolate()))
4708 .FromJust();
4709 info.GetReturnValue().Set(result);
4710}
4711
4712
4713THREADED_TEST(GetOwnPropertyNamesWithInterceptor) {
4714 v8::Isolate* isolate = CcTest::isolate();
4715 v8::HandleScope handle_scope(isolate);
4716 v8::Local<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(isolate);
4717
4718 obj_template->Set(isolate, "7", v8::Integer::New(isolate, 7));
4719 obj_template->Set(isolate, "x", v8::Integer::New(isolate, 42));
4720 obj_template->SetHandler(v8::IndexedPropertyHandlerConfiguration(
4721 nullptr, nullptr, nullptr, nullptr, IndexedPropertyEnumerator));
4722 obj_template->SetHandler(v8::NamedPropertyHandlerConfiguration(
4723 nullptr, nullptr, nullptr, nullptr, NamedPropertyEnumerator));
4724
4725 LocalContext context;
4726 v8::Local<v8::Object> global = context->Global();
4727 global->Set(context.local(), v8_str("object"),
4728 obj_template->NewInstance(context.local()).ToLocalChecked())
4729 .FromJust();
4730
4731 v8::Local<v8::Value> result =
4732 CompileRun("Object.getOwnPropertyNames(object)");
4733 CHECK(result->IsArray());
4734 v8::Local<v8::Array> result_array = v8::Local<v8::Array>::Cast(result);
4735 CHECK_EQ(2u, result_array->Length());
4736 CHECK(result_array->Get(context.local(), 0).ToLocalChecked()->IsString());
4737 CHECK(result_array->Get(context.local(), 1).ToLocalChecked()->IsString());
4738 CHECK(v8_str("7")
4739 ->Equals(context.local(),
4740 result_array->Get(context.local(), 0).ToLocalChecked())
4741 .FromJust());
4742 CHECK(v8_str("x")
4743 ->Equals(context.local(),
4744 result_array->Get(context.local(), 1).ToLocalChecked())
4745 .FromJust());
4746
4747 result = CompileRun("var ret = []; for (var k in object) ret.push(k); ret");
4748 CHECK(result->IsArray());
4749 result_array = v8::Local<v8::Array>::Cast(result);
4750 CHECK_EQ(2u, result_array->Length());
4751 CHECK(result_array->Get(context.local(), 0).ToLocalChecked()->IsString());
4752 CHECK(result_array->Get(context.local(), 1).ToLocalChecked()->IsString());
4753 CHECK(v8_str("7")
4754 ->Equals(context.local(),
4755 result_array->Get(context.local(), 0).ToLocalChecked())
4756 .FromJust());
4757 CHECK(v8_str("x")
4758 ->Equals(context.local(),
4759 result_array->Get(context.local(), 1).ToLocalChecked())
4760 .FromJust());
4761
4762 result = CompileRun("Object.getOwnPropertySymbols(object)");
4763 CHECK(result->IsArray());
4764 result_array = v8::Local<v8::Array>::Cast(result);
4765 CHECK_EQ(1u, result_array->Length());
4766 CHECK(result_array->Get(context.local(), 0)
4767 .ToLocalChecked()
4768 ->Equals(context.local(), v8::Symbol::GetIterator(isolate))
4769 .FromJust());
4770}
4771
4772
4773static void IndexedPropertyEnumeratorException(
4774 const v8::PropertyCallbackInfo<v8::Array>& info) {
4775 info.GetIsolate()->ThrowException(v8_num(42));
4776}
4777
4778
4779THREADED_TEST(GetOwnPropertyNamesWithIndexedInterceptorExceptions_regress4026) {
4780 v8::Isolate* isolate = CcTest::isolate();
4781 v8::HandleScope handle_scope(isolate);
4782 v8::Local<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(isolate);
4783
4784 obj_template->Set(isolate, "7", v8::Integer::New(isolate, 7));
4785 obj_template->Set(isolate, "x", v8::Integer::New(isolate, 42));
4786 // First just try a failing indexed interceptor.
4787 obj_template->SetHandler(v8::IndexedPropertyHandlerConfiguration(
4788 nullptr, nullptr, nullptr, nullptr, IndexedPropertyEnumeratorException));
4789
4790 LocalContext context;
4791 v8::Local<v8::Object> global = context->Global();
4792 global->Set(context.local(), v8_str("object"),
4793 obj_template->NewInstance(context.local()).ToLocalChecked())
4794 .FromJust();
4795 v8::Local<v8::Value> result = CompileRun(
4796 "var result = []; "
4797 "try { "
4798 " for (var k in object) result .push(k);"
4799 "} catch (e) {"
4800 " result = e"
4801 "}"
4802 "result ");
4803 CHECK(!result->IsArray());
4804 CHECK(v8_num(42)->Equals(context.local(), result).FromJust());
4805
4806 result = CompileRun(
4807 "var result = [];"
4808 "try { "
4809 " result = Object.keys(object);"
4810 "} catch (e) {"
4811 " result = e;"
4812 "}"
4813 "result");
4814 CHECK(!result->IsArray());
4815 CHECK(v8_num(42)->Equals(context.local(), result).FromJust());
4816}
4817
4818
4819static void NamedPropertyEnumeratorException(
4820 const v8::PropertyCallbackInfo<v8::Array>& info) {
4821 info.GetIsolate()->ThrowException(v8_num(43));
4822}
4823
4824
4825THREADED_TEST(GetOwnPropertyNamesWithNamedInterceptorExceptions_regress4026) {
4826 v8::Isolate* isolate = CcTest::isolate();
4827 v8::HandleScope handle_scope(isolate);
4828 v8::Local<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(isolate);
4829
4830 obj_template->Set(isolate, "7", v8::Integer::New(isolate, 7));
4831 obj_template->Set(isolate, "x", v8::Integer::New(isolate, 42));
4832 // First just try a failing indexed interceptor.
4833 obj_template->SetHandler(v8::NamedPropertyHandlerConfiguration(
4834 nullptr, nullptr, nullptr, nullptr, NamedPropertyEnumeratorException));
4835
4836 LocalContext context;
4837 v8::Local<v8::Object> global = context->Global();
4838 global->Set(context.local(), v8_str("object"),
4839 obj_template->NewInstance(context.local()).ToLocalChecked())
4840 .FromJust();
4841
4842 v8::Local<v8::Value> result = CompileRun(
4843 "var result = []; "
4844 "try { "
4845 " for (var k in object) result.push(k);"
4846 "} catch (e) {"
4847 " result = e"
4848 "}"
4849 "result");
4850 CHECK(!result->IsArray());
4851 CHECK(v8_num(43)->Equals(context.local(), result).FromJust());
4852
4853 result = CompileRun(
4854 "var result = [];"
4855 "try { "
4856 " result = Object.keys(object);"
4857 "} catch (e) {"
4858 " result = e;"
4859 "}"
4860 "result");
4861 CHECK(!result->IsArray());
4862 CHECK(v8_num(43)->Equals(context.local(), result).FromJust());
4863}
4864
4865namespace {
4866
4867template <typename T>
4868Local<Object> BuildWrappedObject(v8::Isolate* isolate, T* data) {
4869 auto templ = v8::ObjectTemplate::New(isolate);
4870 templ->SetInternalFieldCount(1);
4871 auto instance =
4872 templ->NewInstance(isolate->GetCurrentContext()).ToLocalChecked();
4873 instance->SetAlignedPointerInInternalField(0, data);
4874 return instance;
4875}
4876
4877
4878template <typename T>
4879T* GetWrappedObject(Local<Value> data) {
4880 return reinterpret_cast<T*>(
4881 Object::Cast(*data)->GetAlignedPointerFromInternalField(0));
4882}
4883
4884
4885struct AccessCheckData {
4886 int count;
4887 bool result;
4888};
4889
4890AccessCheckData* g_access_check_data = nullptr;
4891
4892bool SimpleAccessChecker(Local<v8::Context> accessing_context,
4893 Local<v8::Object> access_object,
4894 Local<v8::Value> data) {
4895 g_access_check_data->count++;
4896 return g_access_check_data->result;
4897}
4898
4899
4900struct ShouldInterceptData {
4901 int value;
4902 bool should_intercept;
4903};
4904
4905
4906void ShouldNamedInterceptor(Local<Name> name,
4907 const v8::PropertyCallbackInfo<Value>& info) {
4908 ApiTestFuzzer::Fuzz();
4909 CheckReturnValue(info, FUNCTION_ADDR(ShouldNamedInterceptor));
4910 auto data = GetWrappedObject<ShouldInterceptData>(info.Data());
4911 if (!data->should_intercept) return;
4912 info.GetReturnValue().Set(v8_num(data->value));
4913}
4914
4915
4916void ShouldIndexedInterceptor(uint32_t,
4917 const v8::PropertyCallbackInfo<Value>& info) {
4918 ApiTestFuzzer::Fuzz();
4919 CheckReturnValue(info, FUNCTION_ADDR(ShouldIndexedInterceptor));
4920 auto data = GetWrappedObject<ShouldInterceptData>(info.Data());
4921 if (!data->should_intercept) return;
4922 info.GetReturnValue().Set(v8_num(data->value));
4923}
4924
4925} // namespace
4926
4927
4928TEST(NamedAllCanReadInterceptor) {
4929 auto isolate = CcTest::isolate();
4930 v8::HandleScope handle_scope(isolate);
4931 LocalContext context;
4932
4933 AccessCheckData access_check_data;
4934 access_check_data.result = true;
4935 access_check_data.count = 0;
4936
4937 g_access_check_data = &access_check_data;
4938
4939 ShouldInterceptData intercept_data_0;
4940 intercept_data_0.value = 239;
4941 intercept_data_0.should_intercept = true;
4942
4943 ShouldInterceptData intercept_data_1;
4944 intercept_data_1.value = 165;
4945 intercept_data_1.should_intercept = false;
4946
4947 auto intercepted_0 = v8::ObjectTemplate::New(isolate);
4948 {
4949 v8::NamedPropertyHandlerConfiguration conf(ShouldNamedInterceptor);
4950 conf.flags = v8::PropertyHandlerFlags::kAllCanRead;
4951 conf.data =
4952 BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data_0);
4953 intercepted_0->SetHandler(conf);
4954 }
4955
4956 auto intercepted_1 = v8::ObjectTemplate::New(isolate);
4957 {
4958 v8::NamedPropertyHandlerConfiguration conf(ShouldNamedInterceptor);
4959 conf.flags = v8::PropertyHandlerFlags::kAllCanRead;
4960 conf.data =
4961 BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data_1);
4962 intercepted_1->SetHandler(conf);
4963 }
4964
4965 auto checked = v8::ObjectTemplate::New(isolate);
4966 checked->SetAccessCheckCallback(SimpleAccessChecker);
4967
4968 context->Global()
4969 ->Set(context.local(), v8_str("intercepted_0"),
4970 intercepted_0->NewInstance(context.local()).ToLocalChecked())
4971 .FromJust();
4972 context->Global()
4973 ->Set(context.local(), v8_str("intercepted_1"),
4974 intercepted_1->NewInstance(context.local()).ToLocalChecked())
4975 .FromJust();
4976 auto checked_instance =
4977 checked->NewInstance(context.local()).ToLocalChecked();
4978 checked_instance->Set(context.local(), v8_str("whatever"), v8_num(17))
4979 .FromJust();
4980 context->Global()
4981 ->Set(context.local(), v8_str("checked"), checked_instance)
4982 .FromJust();
4983 CompileRun(
4984 "checked.__proto__ = intercepted_1;"
4985 "intercepted_1.__proto__ = intercepted_0;");
4986
4987 CHECK_EQ(3, access_check_data.count);
4988
4989 ExpectInt32("checked.whatever", 17);
4990 CHECK(!CompileRun("Object.getOwnPropertyDescriptor(checked, 'whatever')")
4991 ->IsUndefined());
4992 CHECK_EQ(5, access_check_data.count);
4993
4994 access_check_data.result = false;
4995 ExpectInt32("checked.whatever", intercept_data_0.value);
4996 {
4997 v8::TryCatch try_catch(isolate);
4998 CompileRun("Object.getOwnPropertyDescriptor(checked, 'whatever')");
4999 CHECK(try_catch.HasCaught());
5000 }
5001 CHECK_EQ(8, access_check_data.count);
5002
5003 intercept_data_1.should_intercept = true;
5004 ExpectInt32("checked.whatever", intercept_data_1.value);
5005 {
5006 v8::TryCatch try_catch(isolate);
5007 CompileRun("Object.getOwnPropertyDescriptor(checked, 'whatever')");
5008 CHECK(try_catch.HasCaught());
5009 }
5010 CHECK_EQ(11, access_check_data.count);
5011 g_access_check_data = nullptr;
5012}
5013
5014
5015TEST(IndexedAllCanReadInterceptor) {
5016 auto isolate = CcTest::isolate();
5017 v8::HandleScope handle_scope(isolate);
5018 LocalContext context;
5019
5020 AccessCheckData access_check_data;
5021 access_check_data.result = true;
5022 access_check_data.count = 0;
5023
5024 g_access_check_data = &access_check_data;
5025
5026 ShouldInterceptData intercept_data_0;
5027 intercept_data_0.value = 239;
5028 intercept_data_0.should_intercept = true;
5029
5030 ShouldInterceptData intercept_data_1;
5031 intercept_data_1.value = 165;
5032 intercept_data_1.should_intercept = false;
5033
5034 auto intercepted_0 = v8::ObjectTemplate::New(isolate);
5035 {
5036 v8::IndexedPropertyHandlerConfiguration conf(ShouldIndexedInterceptor);
5037 conf.flags = v8::PropertyHandlerFlags::kAllCanRead;
5038 conf.data =
5039 BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data_0);
5040 intercepted_0->SetHandler(conf);
5041 }
5042
5043 auto intercepted_1 = v8::ObjectTemplate::New(isolate);
5044 {
5045 v8::IndexedPropertyHandlerConfiguration conf(ShouldIndexedInterceptor);
5046 conf.flags = v8::PropertyHandlerFlags::kAllCanRead;
5047 conf.data =
5048 BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data_1);
5049 intercepted_1->SetHandler(conf);
5050 }
5051
5052 auto checked = v8::ObjectTemplate::New(isolate);
5053 checked->SetAccessCheckCallback(SimpleAccessChecker);
5054
5055 context->Global()
5056 ->Set(context.local(), v8_str("intercepted_0"),
5057 intercepted_0->NewInstance(context.local()).ToLocalChecked())
5058 .FromJust();
5059 context->Global()
5060 ->Set(context.local(), v8_str("intercepted_1"),
5061 intercepted_1->NewInstance(context.local()).ToLocalChecked())
5062 .FromJust();
5063 auto checked_instance =
5064 checked->NewInstance(context.local()).ToLocalChecked();
5065 context->Global()
5066 ->Set(context.local(), v8_str("checked"), checked_instance)
5067 .FromJust();
5068 checked_instance->Set(context.local(), 15, v8_num(17)).FromJust();
5069 CompileRun(
5070 "checked.__proto__ = intercepted_1;"
5071 "intercepted_1.__proto__ = intercepted_0;");
5072
5073 CHECK_EQ(3, access_check_data.count);
5074
5075 access_check_data.result = true;
5076 ExpectInt32("checked[15]", 17);
5077 CHECK(!CompileRun("Object.getOwnPropertyDescriptor(checked, '15')")
5078 ->IsUndefined());
5079 CHECK_EQ(5, access_check_data.count);
5080
5081 access_check_data.result = false;
5082 ExpectInt32("checked[15]", intercept_data_0.value);
5083 {
5084 v8::TryCatch try_catch(isolate);
5085 CompileRun("Object.getOwnPropertyDescriptor(checked, '15')");
5086 CHECK(try_catch.HasCaught());
5087 }
5088 CHECK_EQ(8, access_check_data.count);
5089
5090 intercept_data_1.should_intercept = true;
5091 ExpectInt32("checked[15]", intercept_data_1.value);
5092 {
5093 v8::TryCatch try_catch(isolate);
5094 CompileRun("Object.getOwnPropertyDescriptor(checked, '15')");
5095 CHECK(try_catch.HasCaught());
5096 }
5097 CHECK_EQ(11, access_check_data.count);
5098
5099 g_access_check_data = nullptr;
5100}
5101
5102
5103THREADED_TEST(NonMaskingInterceptorOwnProperty) {
5104 auto isolate = CcTest::isolate();
5105 v8::HandleScope handle_scope(isolate);
5106 LocalContext context;
5107
5108 ShouldInterceptData intercept_data;
5109 intercept_data.value = 239;
5110 intercept_data.should_intercept = true;
5111
5112 auto interceptor_templ = v8::ObjectTemplate::New(isolate);
5113 v8::NamedPropertyHandlerConfiguration conf(ShouldNamedInterceptor);
5114 conf.flags = v8::PropertyHandlerFlags::kNonMasking;
5115 conf.data = BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data);
5116 interceptor_templ->SetHandler(conf);
5117
5118 auto interceptor =
5119 interceptor_templ->NewInstance(context.local()).ToLocalChecked();
5120 context->Global()
5121 ->Set(context.local(), v8_str("obj"), interceptor)
5122 .FromJust();
5123
5124 ExpectInt32("obj.whatever", 239);
5125
5126 CompileRun("obj.whatever = 4;");
5127
5128 // obj.whatever exists, thus it is not affected by the non-masking
5129 // interceptor.
5130 ExpectInt32("obj.whatever", 4);
5131
5132 CompileRun("delete obj.whatever;");
5133 ExpectInt32("obj.whatever", 239);
5134}
5135
5136
5137THREADED_TEST(NonMaskingInterceptorPrototypeProperty) {
5138 auto isolate = CcTest::isolate();
5139 v8::HandleScope handle_scope(isolate);
5140 LocalContext context;
5141
5142 ShouldInterceptData intercept_data;
5143 intercept_data.value = 239;
5144 intercept_data.should_intercept = true;
5145
5146 auto interceptor_templ = v8::ObjectTemplate::New(isolate);
5147 v8::NamedPropertyHandlerConfiguration conf(ShouldNamedInterceptor);
5148 conf.flags = v8::PropertyHandlerFlags::kNonMasking;
5149 conf.data = BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data);
5150 interceptor_templ->SetHandler(conf);
5151
5152 auto interceptor =
5153 interceptor_templ->NewInstance(context.local()).ToLocalChecked();
5154 context->Global()
5155 ->Set(context.local(), v8_str("obj"), interceptor)
5156 .FromJust();
5157
5158 ExpectInt32("obj.whatever", 239);
5159
5160 CompileRun("obj.__proto__ = {'whatever': 4};");
5161 ExpectInt32("obj.whatever", 4);
5162
5163 CompileRun("delete obj.__proto__.whatever;");
5164 ExpectInt32("obj.whatever", 239);
5165}
5166
5167
5168THREADED_TEST(NonMaskingInterceptorPrototypePropertyIC) {
5169 auto isolate = CcTest::isolate();
5170 v8::HandleScope handle_scope(isolate);
5171 LocalContext context;
5172
5173 ShouldInterceptData intercept_data;
5174 intercept_data.value = 239;
5175 intercept_data.should_intercept = true;
5176
5177 auto interceptor_templ = v8::ObjectTemplate::New(isolate);
5178 v8::NamedPropertyHandlerConfiguration conf(ShouldNamedInterceptor);
5179 conf.flags = v8::PropertyHandlerFlags::kNonMasking;
5180 conf.data = BuildWrappedObject<ShouldInterceptData>(isolate, &intercept_data);
5181 interceptor_templ->SetHandler(conf);
5182
5183 auto interceptor =
5184 interceptor_templ->NewInstance(context.local()).ToLocalChecked();
5185 context->Global()
5186 ->Set(context.local(), v8_str("obj"), interceptor)
5187 .FromJust();
5188
5189 CompileRun(
5190 "outer = {};"
5191 "outer.__proto__ = obj;"
5192 "function f(obj) {"
5193 " var x;"
5194 " for (var i = 0; i < 4; i++) {"
5195 " x = obj.whatever;"
5196 " }"
5197 " return x;"
5198 "}");
5199
5200 // Receiver == holder.
5201 CompileRun("obj.__proto__ = null;");
5202 ExpectInt32("f(obj)", 239);
5203 ExpectInt32("f(outer)", 239);
5204
5205 // Receiver != holder.
5206 CompileRun("Object.setPrototypeOf(obj, {});");
5207 ExpectInt32("f(obj)", 239);
5208 ExpectInt32("f(outer)", 239);
5209
5210 // Masked value on prototype.
5211 CompileRun("obj.__proto__.whatever = 4;");
5212 CompileRun("obj.__proto__.__proto__ = { 'whatever' : 5 };");
5213 ExpectInt32("f(obj)", 4);
5214 ExpectInt32("f(outer)", 4);
5215
5216 // Masked value on prototype prototype.
5217 CompileRun("delete obj.__proto__.whatever;");
5218 ExpectInt32("f(obj)", 5);
5219 ExpectInt32("f(outer)", 5);
5220
5221 // Reset.
5222 CompileRun("delete obj.__proto__.__proto__.whatever;");
5223 ExpectInt32("f(obj)", 239);
5224 ExpectInt32("f(outer)", 239);
5225
5226 // Masked value on self.
5227 CompileRun("obj.whatever = 4;");
5228 ExpectInt32("f(obj)", 4);
5229 ExpectInt32("f(outer)", 4);
5230
5231 // Reset.
5232 CompileRun("delete obj.whatever;");
5233 ExpectInt32("f(obj)", 239);
5234 ExpectInt32("f(outer)", 239);
5235
5236 CompileRun("outer.whatever = 4;");
5237 ExpectInt32("f(obj)", 239);
5238 ExpectInt32("f(outer)", 4);
5239}
5240
5241namespace {
5242
5243void ConcatNamedPropertyGetter(
5244 Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
5245 info.GetReturnValue().Set(
5246 // Return the property name concatenated with itself.
5247 String::Concat(info.GetIsolate(), name.As<String>(), name.As<String>()));
5248}
5249
5250void ConcatIndexedPropertyGetter(
5251 uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
5252 info.GetReturnValue().Set(
5253 // Return the double value of the index.
5254 v8_num(index + index));
5255}
5256
5257void EnumCallbackWithNames(const v8::PropertyCallbackInfo<v8::Array>& info) {
5258 ApiTestFuzzer::Fuzz();
5259 v8::Local<v8::Array> result = v8::Array::New(info.GetIsolate(), 4);
5260 v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
5261 CHECK(
5262 result
5263 ->Set(context, v8::Integer::New(info.GetIsolate(), 0), v8_str("foo"))
5264 .FromJust());
5265 CHECK(
5266 result
5267 ->Set(context, v8::Integer::New(info.GetIsolate(), 1), v8_str("bar"))
5268 .FromJust());
5269 CHECK(
5270 result
5271 ->Set(context, v8::Integer::New(info.GetIsolate(), 2), v8_str("baz"))
5272 .FromJust());
5273 CHECK(
5274 result->Set(context, v8::Integer::New(info.GetIsolate(), 3), v8_str("10"))
5275 .FromJust());
5276
5277 // Create a holey array.
5278 CHECK(result->Delete(context, v8::Integer::New(info.GetIsolate(), 1))
5279 .FromJust());
5280 info.GetReturnValue().Set(result);
5281}
5282
5283void EnumCallbackWithIndices(const v8::PropertyCallbackInfo<v8::Array>& info) {
5284 ApiTestFuzzer::Fuzz();
5285 v8::Local<v8::Array> result = v8::Array::New(info.GetIsolate(), 4);
5286 v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
5287
5288 CHECK(result->Set(context, v8::Integer::New(info.GetIsolate(), 0), v8_num(10))
5289 .FromJust());
5290 CHECK(result->Set(context, v8::Integer::New(info.GetIsolate(), 1), v8_num(11))
5291 .FromJust());
5292 CHECK(result->Set(context, v8::Integer::New(info.GetIsolate(), 2), v8_num(12))
5293 .FromJust());
5294 CHECK(result->Set(context, v8::Integer::New(info.GetIsolate(), 3), v8_num(14))
5295 .FromJust());
5296
5297 // Create a holey array.
5298 CHECK(result->Delete(context, v8::Integer::New(info.GetIsolate(), 1))
5299 .FromJust());
5300 info.GetReturnValue().Set(result);
5301}
5302
5303void RestrictiveNamedQuery(Local<Name> property,
5304 const v8::PropertyCallbackInfo<v8::Integer>& info) {
5305 // Only "foo" is enumerable.
5306 if (v8_str("foo")
5307 ->Equals(info.GetIsolate()->GetCurrentContext(), property)
5308 .FromJust()) {
5309 info.GetReturnValue().Set(v8::PropertyAttribute::None);
5310 return;
5311 }
5312 info.GetReturnValue().Set(v8::PropertyAttribute::DontEnum);
5313}
5314
5315void RestrictiveIndexedQuery(
5316 uint32_t index, const v8::PropertyCallbackInfo<v8::Integer>& info) {
5317 // Only index 2 and 12 are enumerable.
5318 if (index == 2 || index == 12) {
5319 info.GetReturnValue().Set(v8::PropertyAttribute::None);
5320 return;
5321 }
5322 info.GetReturnValue().Set(v8::PropertyAttribute::DontEnum);
5323}
5324} // namespace
5325
5326// Regression test for V8 bug 6627.
5327// Object.keys() must return enumerable keys only.
5328THREADED_TEST(EnumeratorsAndUnenumerableNamedProperties) {
5329 // The enumerator interceptor returns a list
5330 // of items which are filtered according to the
5331 // properties defined in the query interceptor.
5332 v8::Isolate* isolate = CcTest::isolate();
5333 v8::HandleScope scope(isolate);
5334 v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate);
5335 obj->SetHandler(v8::NamedPropertyHandlerConfiguration(
5336 ConcatNamedPropertyGetter, nullptr, RestrictiveNamedQuery, nullptr,
5337 EnumCallbackWithNames));
5338 LocalContext context;
5339 context->Global()
5340 ->Set(context.local(), v8_str("obj"),
5341 obj->NewInstance(context.local()).ToLocalChecked())
5342 .FromJust();
5343
5344 ExpectInt32("Object.getOwnPropertyNames(obj).length", 3);
5345 ExpectString("Object.getOwnPropertyNames(obj)[0]", "foo");
5346 ExpectString("Object.getOwnPropertyNames(obj)[1]", "baz");
5347 ExpectString("Object.getOwnPropertyNames(obj)[2]", "10");
5348
5349 ExpectTrue("Object.getOwnPropertyDescriptor(obj, 'foo').enumerable");
5350 ExpectFalse("Object.getOwnPropertyDescriptor(obj, 'baz').enumerable");
5351
5352 ExpectInt32("Object.entries(obj).length", 1);
5353 ExpectString("Object.entries(obj)[0][0]", "foo");
5354 ExpectString("Object.entries(obj)[0][1]", "foofoo");
5355
5356 ExpectInt32("Object.keys(obj).length", 1);
5357 ExpectString("Object.keys(obj)[0]", "foo");
5358
5359 ExpectInt32("Object.values(obj).length", 1);
5360 ExpectString("Object.values(obj)[0]", "foofoo");
5361}
5362
5363namespace {
5364void QueryInterceptorForFoo(Local<Name> property,
5365 const v8::PropertyCallbackInfo<v8::Integer>& info) {
5366 // Don't intercept anything except "foo."
5367 if (!v8_str("foo")
5368 ->Equals(info.GetIsolate()->GetCurrentContext(), property)
5369 .FromJust()) {
5370 return;
5371 }
5372 // "foo" is enumerable.
5373 info.GetReturnValue().Set(v8::PropertyAttribute::None);
5374}
5375} // namespace
5376
5377// Test that calls to the query interceptor are independent of each
5378// other.
5379THREADED_TEST(EnumeratorsAndUnenumerableNamedPropertiesWithoutSet) {
5380 // The enumerator interceptor returns a list
5381 // of items which are filtered according to the
5382 // properties defined in the query interceptor.
5383 v8::Isolate* isolate = CcTest::isolate();
5384 v8::HandleScope scope(isolate);
5385 v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate);
5386 obj->SetHandler(v8::NamedPropertyHandlerConfiguration(
5387 ConcatNamedPropertyGetter, nullptr, QueryInterceptorForFoo, nullptr,
5388 EnumCallbackWithNames));
5389 LocalContext context;
5390 context->Global()
5391 ->Set(context.local(), v8_str("obj"),
5392 obj->NewInstance(context.local()).ToLocalChecked())
5393 .FromJust();
5394
5395 ExpectInt32("Object.getOwnPropertyNames(obj).length", 3);
5396 ExpectString("Object.getOwnPropertyNames(obj)[0]", "foo");
5397 ExpectString("Object.getOwnPropertyNames(obj)[1]", "baz");
5398 ExpectString("Object.getOwnPropertyNames(obj)[2]", "10");
5399
5400 ExpectTrue("Object.getOwnPropertyDescriptor(obj, 'foo').enumerable");
5401 ExpectInt32("Object.keys(obj).length", 1);
5402}
5403
5404THREADED_TEST(EnumeratorsAndUnenumerableIndexedPropertiesArgumentsElements) {
5405 v8::Isolate* isolate = CcTest::isolate();
5406 v8::HandleScope scope(isolate);
5407 v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate);
5408 obj->SetHandler(v8::IndexedPropertyHandlerConfiguration(
5409 ConcatIndexedPropertyGetter, nullptr, RestrictiveIndexedQuery, nullptr,
5410 SloppyArgsIndexedPropertyEnumerator));
5411 LocalContext context;
5412 context->Global()
5413 ->Set(context.local(), v8_str("obj"),
5414 obj->NewInstance(context.local()).ToLocalChecked())
5415 .FromJust();
5416
5417 ExpectInt32("Object.getOwnPropertyNames(obj).length", 4);
5418 ExpectString("Object.getOwnPropertyNames(obj)[0]", "0");
5419 ExpectString("Object.getOwnPropertyNames(obj)[1]", "1");
5420 ExpectString("Object.getOwnPropertyNames(obj)[2]", "2");
5421 ExpectString("Object.getOwnPropertyNames(obj)[3]", "3");
5422
5423 ExpectTrue("Object.getOwnPropertyDescriptor(obj, '2').enumerable");
5424
5425 ExpectInt32("Object.entries(obj).length", 1);
5426 ExpectString("Object.entries(obj)[0][0]", "2");
5427 ExpectInt32("Object.entries(obj)[0][1]", 4);
5428
5429 ExpectInt32("Object.keys(obj).length", 1);
5430 ExpectString("Object.keys(obj)[0]", "2");
5431
5432 ExpectInt32("Object.values(obj).length", 1);
5433 ExpectInt32("Object.values(obj)[0]", 4);
5434}
5435
5436THREADED_TEST(EnumeratorsAndUnenumerableIndexedProperties) {
5437 // The enumerator interceptor returns a list
5438 // of items which are filtered according to the
5439 // properties defined in the query interceptor.
5440 v8::Isolate* isolate = CcTest::isolate();
5441 v8::HandleScope scope(isolate);
5442 v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate);
5443 obj->SetHandler(v8::IndexedPropertyHandlerConfiguration(
5444 ConcatIndexedPropertyGetter, nullptr, RestrictiveIndexedQuery, nullptr,
5445 EnumCallbackWithIndices));
5446 LocalContext context;
5447 context->Global()
5448 ->Set(context.local(), v8_str("obj"),
5449 obj->NewInstance(context.local()).ToLocalChecked())
5450 .FromJust();
5451
5452 ExpectInt32("Object.getOwnPropertyNames(obj).length", 3);
5453 ExpectString("Object.getOwnPropertyNames(obj)[0]", "10");
5454 ExpectString("Object.getOwnPropertyNames(obj)[1]", "12");
5455 ExpectString("Object.getOwnPropertyNames(obj)[2]", "14");
5456
5457 ExpectFalse("Object.getOwnPropertyDescriptor(obj, '10').enumerable");
5458 ExpectTrue("Object.getOwnPropertyDescriptor(obj, '12').enumerable");
5459
5460 ExpectInt32("Object.entries(obj).length", 1);
5461 ExpectString("Object.entries(obj)[0][0]", "12");
5462 ExpectInt32("Object.entries(obj)[0][1]", 24);
5463
5464 ExpectInt32("Object.keys(obj).length", 1);
5465 ExpectString("Object.keys(obj)[0]", "12");
5466
5467 ExpectInt32("Object.values(obj).length", 1);
5468 ExpectInt32("Object.values(obj)[0]", 24);
5469}
5470
5471THREADED_TEST(EnumeratorsAndForIn) {
5472 v8::Isolate* isolate = CcTest::isolate();
5473 v8::HandleScope scope(isolate);
5474 v8::Local<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate);
5475 obj->SetHandler(v8::NamedPropertyHandlerConfiguration(
5476 ConcatNamedPropertyGetter, nullptr, RestrictiveNamedQuery, nullptr,
5477 NamedEnum));
5478 LocalContext context;
5479 context->Global()
5480 ->Set(context.local(), v8_str("obj"),
5481 obj->NewInstance(context.local()).ToLocalChecked())
5482 .FromJust();
5483
5484 ExpectInt32("Object.getOwnPropertyNames(obj).length", 3);
5485 ExpectString("Object.getOwnPropertyNames(obj)[0]", "foo");
5486
5487 ExpectTrue("Object.getOwnPropertyDescriptor(obj, 'foo').enumerable");
5488
5489 CompileRun(
5490 "let concat = '';"
5491 "for(var prop in obj) {"
5492 " concat += `key:${prop}:value:${obj[prop]}`;"
5493 "}");
5494
5495 // Check that for...in only iterates over enumerable properties.
5496 ExpectString("concat", "key:foo:value:foofoo");
5497}
5498
5499namespace {
5500
5501void DatabaseGetter(Local<Name> name,
5502 const v8::PropertyCallbackInfo<Value>& info) {
5503 ApiTestFuzzer::Fuzz();
5504 auto context = info.GetIsolate()->GetCurrentContext();
5505 Local<v8::Object> db = info.Holder()
5506 ->GetRealNamedProperty(context, v8_str("db"))
5507 .ToLocalChecked()
5508 .As<v8::Object>();
5509 if (!db->Has(context, name).FromJust()) return;
5510 info.GetReturnValue().Set(db->Get(context, name).ToLocalChecked());
5511}
5512
5513
5514void DatabaseSetter(Local<Name> name, Local<Value> value,
5515 const v8::PropertyCallbackInfo<Value>& info) {
5516 ApiTestFuzzer::Fuzz();
5517 auto context = info.GetIsolate()->GetCurrentContext();
5518 if (name->Equals(context, v8_str("db")).FromJust()) return;
5519 Local<v8::Object> db = info.Holder()
5520 ->GetRealNamedProperty(context, v8_str("db"))
5521 .ToLocalChecked()
5522 .As<v8::Object>();
5523 db->Set(context, name, value).FromJust();
5524 info.GetReturnValue().Set(value);
5525}
5526
5527} // namespace
5528
5529
5530THREADED_TEST(NonMaskingInterceptorGlobalEvalRegression) {
5531 auto isolate = CcTest::isolate();
5532 v8::HandleScope handle_scope(isolate);
5533 LocalContext context;
5534
5535 auto interceptor_templ = v8::ObjectTemplate::New(isolate);
5536 v8::NamedPropertyHandlerConfiguration conf(DatabaseGetter, DatabaseSetter);
5537 conf.flags = v8::PropertyHandlerFlags::kNonMasking;
5538 interceptor_templ->SetHandler(conf);
5539
5540 context->Global()
5541 ->Set(context.local(), v8_str("intercepted_1"),
5542 interceptor_templ->NewInstance(context.local()).ToLocalChecked())
5543 .FromJust();
5544 context->Global()
5545 ->Set(context.local(), v8_str("intercepted_2"),
5546 interceptor_templ->NewInstance(context.local()).ToLocalChecked())
5547 .FromJust();
5548
5549 // Init dbs.
5550 CompileRun(
5551 "intercepted_1.db = {};"
5552 "intercepted_2.db = {};");
5553
5554 ExpectInt32(
5555 "var obj = intercepted_1;"
5556 "obj.x = 4;"
5557 "eval('obj.x');"
5558 "eval('obj.x');"
5559 "eval('obj.x');"
5560 "obj = intercepted_2;"
5561 "obj.x = 9;"
5562 "eval('obj.x');",
5563 9);
5564}
5565
5566static void CheckReceiver(Local<Name> name,
5567 const v8::PropertyCallbackInfo<v8::Value>& info) {
5568 CHECK(info.This()->IsObject());
5569}
5570
5571TEST(Regress609134Interceptor) {
5572 LocalContext env;
5573 v8::Isolate* isolate = env->GetIsolate();
5574 v8::HandleScope scope(isolate);
5575 auto fun_templ = v8::FunctionTemplate::New(isolate);
5576 fun_templ->InstanceTemplate()->SetHandler(
5577 v8::NamedPropertyHandlerConfiguration(CheckReceiver));
5578
5579 CHECK(env->Global()
5580 ->Set(env.local(), v8_str("Fun"),
5581 fun_templ->GetFunction(env.local()).ToLocalChecked())
5582 .FromJust());
5583
5584 CompileRun(
5585 "var f = new Fun();"
5586 "Number.prototype.__proto__ = f;"
5587 "var a = 42;"
5588 "for (var i = 0; i<3; i++) { a.foo; }");
5589}