v8  3.25.30(node0.11.13)
V8 is Google's open source JavaScript engine
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
test-object-observe.cc
Go to the documentation of this file.
1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 
28 #include "v8.h"
29 
30 #include "cctest.h"
31 
32 using namespace v8;
33 namespace i = v8::internal;
34 
35 
36 TEST(PerIsolateState) {
38  LocalContext context1(CcTest::isolate());
39  CompileRun(
40  "var count = 0;"
41  "var calls = 0;"
42  "var observer = function(records) { count = records.length; calls++ };"
43  "var obj = {};"
44  "Object.observe(obj, observer);");
45  Handle<Value> observer = CompileRun("observer");
46  Handle<Value> obj = CompileRun("obj");
47  Handle<Value> notify_fun1 = CompileRun(
48  "(function() { obj.foo = 'bar'; })");
49  Handle<Value> notify_fun2;
50  {
51  LocalContext context2(CcTest::isolate());
52  context2->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "obj"),
53  obj);
54  notify_fun2 = CompileRun(
55  "(function() { obj.foo = 'baz'; })");
56  }
57  Handle<Value> notify_fun3;
58  {
59  LocalContext context3(CcTest::isolate());
60  context3->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "obj"),
61  obj);
62  notify_fun3 = CompileRun(
63  "(function() { obj.foo = 'bat'; })");
64  }
65  {
66  LocalContext context4(CcTest::isolate());
67  context4->Global()->Set(
68  String::NewFromUtf8(CcTest::isolate(), "observer"), observer);
69  context4->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "fun1"),
70  notify_fun1);
71  context4->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "fun2"),
72  notify_fun2);
73  context4->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "fun3"),
74  notify_fun3);
75  CompileRun("fun1(); fun2(); fun3(); Object.deliverChangeRecords(observer)");
76  }
77  CHECK_EQ(1, CompileRun("calls")->Int32Value());
78  CHECK_EQ(3, CompileRun("count")->Int32Value());
79 }
80 
81 
82 TEST(EndOfMicrotaskDelivery) {
84  LocalContext context(CcTest::isolate());
85  CompileRun(
86  "var obj = {};"
87  "var count = 0;"
88  "var observer = function(records) { count = records.length };"
89  "Object.observe(obj, observer);"
90  "obj.foo = 'bar';");
91  CHECK_EQ(1, CompileRun("count")->Int32Value());
92 }
93 
94 
95 TEST(DeliveryOrdering) {
97  LocalContext context(CcTest::isolate());
98  CompileRun(
99  "var obj1 = {};"
100  "var obj2 = {};"
101  "var ordering = [];"
102  "function observer2() { ordering.push(2); };"
103  "function observer1() { ordering.push(1); };"
104  "function observer3() { ordering.push(3); };"
105  "Object.observe(obj1, observer1);"
106  "Object.observe(obj1, observer2);"
107  "Object.observe(obj1, observer3);"
108  "obj1.foo = 'bar';");
109  CHECK_EQ(3, CompileRun("ordering.length")->Int32Value());
110  CHECK_EQ(1, CompileRun("ordering[0]")->Int32Value());
111  CHECK_EQ(2, CompileRun("ordering[1]")->Int32Value());
112  CHECK_EQ(3, CompileRun("ordering[2]")->Int32Value());
113  CompileRun(
114  "ordering = [];"
115  "Object.observe(obj2, observer3);"
116  "Object.observe(obj2, observer2);"
117  "Object.observe(obj2, observer1);"
118  "obj2.foo = 'baz'");
119  CHECK_EQ(3, CompileRun("ordering.length")->Int32Value());
120  CHECK_EQ(1, CompileRun("ordering[0]")->Int32Value());
121  CHECK_EQ(2, CompileRun("ordering[1]")->Int32Value());
122  CHECK_EQ(3, CompileRun("ordering[2]")->Int32Value());
123 }
124 
125 
126 TEST(DeliveryOrderingReentrant) {
127  HandleScope scope(CcTest::isolate());
128  LocalContext context(CcTest::isolate());
129  CompileRun(
130  "var obj = {};"
131  "var reentered = false;"
132  "var ordering = [];"
133  "function observer1() { ordering.push(1); };"
134  "function observer2() {"
135  " if (!reentered) {"
136  " obj.foo = 'baz';"
137  " reentered = true;"
138  " }"
139  " ordering.push(2);"
140  "};"
141  "function observer3() { ordering.push(3); };"
142  "Object.observe(obj, observer1);"
143  "Object.observe(obj, observer2);"
144  "Object.observe(obj, observer3);"
145  "obj.foo = 'bar';");
146  CHECK_EQ(5, CompileRun("ordering.length")->Int32Value());
147  CHECK_EQ(1, CompileRun("ordering[0]")->Int32Value());
148  CHECK_EQ(2, CompileRun("ordering[1]")->Int32Value());
149  CHECK_EQ(3, CompileRun("ordering[2]")->Int32Value());
150  // Note that we re-deliver to observers 1 and 2, while observer3
151  // already received the second record during the first round.
152  CHECK_EQ(1, CompileRun("ordering[3]")->Int32Value());
153  CHECK_EQ(2, CompileRun("ordering[1]")->Int32Value());
154 }
155 
156 
157 TEST(DeliveryOrderingDeliverChangeRecords) {
158  HandleScope scope(CcTest::isolate());
159  LocalContext context(CcTest::isolate());
160  CompileRun(
161  "var obj = {};"
162  "var ordering = [];"
163  "function observer1() { ordering.push(1); if (!obj.b) obj.b = true };"
164  "function observer2() { ordering.push(2); };"
165  "Object.observe(obj, observer1);"
166  "Object.observe(obj, observer2);"
167  "obj.a = 1;"
168  "Object.deliverChangeRecords(observer2);");
169  CHECK_EQ(4, CompileRun("ordering.length")->Int32Value());
170  // First, observer2 is called due to deliverChangeRecords
171  CHECK_EQ(2, CompileRun("ordering[0]")->Int32Value());
172  // Then, observer1 is called when the stack unwinds
173  CHECK_EQ(1, CompileRun("ordering[1]")->Int32Value());
174  // observer1's mutation causes both 1 and 2 to be reactivated,
175  // with 1 having priority.
176  CHECK_EQ(1, CompileRun("ordering[2]")->Int32Value());
177  CHECK_EQ(2, CompileRun("ordering[3]")->Int32Value());
178 }
179 
180 
181 TEST(ObjectHashTableGrowth) {
182  HandleScope scope(CcTest::isolate());
183  // Initializing this context sets up initial hash tables.
184  LocalContext context(CcTest::isolate());
185  Handle<Value> obj = CompileRun("obj = {};");
186  Handle<Value> observer = CompileRun(
187  "var ran = false;"
188  "(function() { ran = true })");
189  {
190  // As does initializing this context.
191  LocalContext context2(CcTest::isolate());
192  context2->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "obj"),
193  obj);
194  context2->Global()->Set(
195  String::NewFromUtf8(CcTest::isolate(), "observer"), observer);
196  CompileRun(
197  "var objArr = [];"
198  // 100 objects should be enough to make the hash table grow
199  // (and thus relocate).
200  "for (var i = 0; i < 100; ++i) {"
201  " objArr.push({});"
202  " Object.observe(objArr[objArr.length-1], function(){});"
203  "}"
204  "Object.observe(obj, observer);");
205  }
206  // obj is now marked "is_observed", but our map has moved.
207  CompileRun("obj.foo = 'bar'");
208  CHECK(CompileRun("ran")->BooleanValue());
209 }
210 
211 
212 TEST(GlobalObjectObservation) {
213  LocalContext context(CcTest::isolate());
214  HandleScope scope(CcTest::isolate());
215  Handle<Object> global_proxy = context->Global();
216  CompileRun(
217  "var records = [];"
218  "var global = this;"
219  "Object.observe(global, function(r) { [].push.apply(records, r) });"
220  "global.foo = 'hello';");
221  CHECK_EQ(1, CompileRun("records.length")->Int32Value());
222  CHECK(global_proxy->StrictEquals(CompileRun("records[0].object")));
223 
224  // Detached, mutating the proxy has no effect.
225  context->DetachGlobal();
226  CompileRun("global.bar = 'goodbye';");
227  CHECK_EQ(1, CompileRun("records.length")->Int32Value());
228  CompileRun("this.baz = 'goodbye';");
229  CHECK_EQ(1, CompileRun("records.length")->Int32Value());
230 
231  // Attached to a different context, should not leak mutations
232  // to the old context.
233  context->DetachGlobal();
234  {
235  LocalContext context2(CcTest::isolate());
236  CompileRun(
237  "var records2 = [];"
238  "var global = this;"
239  "Object.observe(this, function(r) { [].push.apply(records2, r) });"
240  "this.v1 = 'context2';");
241  context2->DetachGlobal();
242  CompileRun(
243  "global.v2 = 'context2';"
244  "this.v3 = 'context2';");
245  CHECK_EQ(1, CompileRun("records2.length")->Int32Value());
246  }
247  CHECK_EQ(1, CompileRun("records.length")->Int32Value());
248 
249  // Attaching by passing to Context::New
250  {
251  // Delegates to Context::New
252  LocalContext context3(
253  CcTest::isolate(), NULL, Handle<ObjectTemplate>(), global_proxy);
254  CompileRun(
255  "var records3 = [];"
256  "Object.observe(this, function(r) { [].push.apply(records3, r) });"
257  "this.qux = 'context3';");
258  CHECK_EQ(1, CompileRun("records3.length")->Int32Value());
259  CHECK(global_proxy->StrictEquals(CompileRun("records3[0].object")));
260  }
261  CHECK_EQ(1, CompileRun("records.length")->Int32Value());
262 }
263 
264 
267  const char* type;
268  const char* name;
270 };
271 
272 
273 // TODO(adamk): Use this helper elsewhere in this file.
274 static void ExpectRecords(v8::Isolate* isolate,
275  Handle<Value> records,
276  const RecordExpectation expectations[],
277  int num) {
278  CHECK(records->IsArray());
279  Handle<Array> recordArray = records.As<Array>();
280  CHECK_EQ(num, static_cast<int>(recordArray->Length()));
281  for (int i = 0; i < num; ++i) {
282  Handle<Value> record = recordArray->Get(i);
283  CHECK(record->IsObject());
284  Handle<Object> recordObj = record.As<Object>();
285  CHECK(expectations[i].object->StrictEquals(
286  recordObj->Get(String::NewFromUtf8(isolate, "object"))));
287  CHECK(String::NewFromUtf8(isolate, expectations[i].type)->Equals(
288  recordObj->Get(String::NewFromUtf8(isolate, "type"))));
289  if (strcmp("splice", expectations[i].type) != 0) {
290  CHECK(String::NewFromUtf8(isolate, expectations[i].name)->Equals(
291  recordObj->Get(String::NewFromUtf8(isolate, "name"))));
292  if (!expectations[i].old_value.IsEmpty()) {
293  CHECK(expectations[i].old_value->Equals(
294  recordObj->Get(String::NewFromUtf8(isolate, "oldValue"))));
295  }
296  }
297  }
298 }
299 
300 #define EXPECT_RECORDS(records, expectations) \
301  ExpectRecords(CcTest::isolate(), records, expectations, \
302  ARRAY_SIZE(expectations))
303 
304 TEST(APITestBasicMutation) {
305  v8::Isolate* v8_isolate = CcTest::isolate();
306  HandleScope scope(v8_isolate);
307  LocalContext context(v8_isolate);
309  "var records = [];"
310  "var obj = {};"
311  "function observer(r) { [].push.apply(records, r); };"
312  "Object.observe(obj, observer);"
313  "obj"));
314  obj->Set(String::NewFromUtf8(v8_isolate, "foo"),
315  Number::New(v8_isolate, 7));
316  obj->Set(1, Number::New(v8_isolate, 2));
317  // ForceSet should work just as well as Set
318  obj->ForceSet(String::NewFromUtf8(v8_isolate, "foo"),
319  Number::New(v8_isolate, 3));
320  obj->ForceSet(Number::New(v8_isolate, 1), Number::New(v8_isolate, 4));
321  // Setting an indexed element via the property setting method
322  obj->Set(Number::New(v8_isolate, 1), Number::New(v8_isolate, 5));
323  // Setting with a non-String, non-uint32 key
324  obj->Set(Number::New(v8_isolate, 1.1),
325  Number::New(v8_isolate, 6), DontDelete);
326  obj->Delete(String::NewFromUtf8(v8_isolate, "foo"));
327  obj->Delete(1);
328  obj->ForceDelete(Number::New(v8_isolate, 1.1));
329 
330  // Force delivery
331  // TODO(adamk): Should the above set methods trigger delivery themselves?
332  CompileRun("void 0");
333  CHECK_EQ(9, CompileRun("records.length")->Int32Value());
334  const RecordExpectation expected_records[] = {
335  { obj, "add", "foo", Handle<Value>() },
336  { obj, "add", "1", Handle<Value>() },
337  // Note: use 7 not 1 below, as the latter triggers a nifty VS10 compiler bug
338  // where instead of 1.0, a garbage value would be passed into Number::New.
339  { obj, "update", "foo", Number::New(v8_isolate, 7) },
340  { obj, "update", "1", Number::New(v8_isolate, 2) },
341  { obj, "update", "1", Number::New(v8_isolate, 4) },
342  { obj, "add", "1.1", Handle<Value>() },
343  { obj, "delete", "foo", Number::New(v8_isolate, 3) },
344  { obj, "delete", "1", Number::New(v8_isolate, 5) },
345  { obj, "delete", "1.1", Number::New(v8_isolate, 6) }
346  };
347  EXPECT_RECORDS(CompileRun("records"), expected_records);
348 }
349 
350 
351 TEST(HiddenPrototypeObservation) {
352  v8::Isolate* v8_isolate = CcTest::isolate();
353  HandleScope scope(v8_isolate);
354  LocalContext context(v8_isolate);
356  tmpl->SetHiddenPrototype(true);
357  tmpl->InstanceTemplate()->Set(
358  String::NewFromUtf8(v8_isolate, "foo"), Number::New(v8_isolate, 75));
359  Handle<Object> proto = tmpl->GetFunction()->NewInstance();
360  Handle<Object> obj = Object::New(v8_isolate);
361  obj->SetPrototype(proto);
362  context->Global()->Set(String::NewFromUtf8(v8_isolate, "obj"), obj);
363  context->Global()->Set(String::NewFromUtf8(v8_isolate, "proto"),
364  proto);
365  CompileRun(
366  "var records;"
367  "function observer(r) { records = r; };"
368  "Object.observe(obj, observer);"
369  "obj.foo = 41;" // triggers a notification
370  "proto.foo = 42;"); // does not trigger a notification
371  const RecordExpectation expected_records[] = {
372  { obj, "update", "foo", Number::New(v8_isolate, 75) }
373  };
374  EXPECT_RECORDS(CompileRun("records"), expected_records);
375  obj->SetPrototype(Null(v8_isolate));
376  CompileRun("obj.foo = 43");
377  const RecordExpectation expected_records2[] = {
378  { obj, "add", "foo", Handle<Value>() }
379  };
380  EXPECT_RECORDS(CompileRun("records"), expected_records2);
381  obj->SetPrototype(proto);
382  CompileRun(
383  "Object.observe(proto, observer);"
384  "proto.bar = 1;"
385  "Object.unobserve(obj, observer);"
386  "obj.foo = 44;");
387  const RecordExpectation expected_records3[] = {
388  { proto, "add", "bar", Handle<Value>() }
389  // TODO(adamk): The below record should be emitted since proto is observed
390  // and has been modified. Not clear if this happens in practice.
391  // { proto, "update", "foo", Number::New(43) }
392  };
393  EXPECT_RECORDS(CompileRun("records"), expected_records3);
394 }
395 
396 
397 static int NumberOfElements(i::Handle<i::JSWeakMap> map) {
398  return i::ObjectHashTable::cast(map->table())->NumberOfElements();
399 }
400 
401 
402 TEST(ObservationWeakMap) {
403  HandleScope scope(CcTest::isolate());
404  LocalContext context(CcTest::isolate());
405  CompileRun(
406  "var obj = {};"
407  "Object.observe(obj, function(){});"
408  "Object.getNotifier(obj);"
409  "obj = null;");
410  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(CcTest::isolate());
411  i::Handle<i::JSObject> observation_state =
412  i_isolate->factory()->observation_state();
413  i::Handle<i::JSWeakMap> callbackInfoMap =
415  i::GetProperty(observation_state, "callbackInfoMap"));
416  i::Handle<i::JSWeakMap> objectInfoMap =
418  i::GetProperty(observation_state, "objectInfoMap"));
419  i::Handle<i::JSWeakMap> notifierObjectInfoMap =
421  i::GetProperty(observation_state, "notifierObjectInfoMap"));
422  CHECK_EQ(1, NumberOfElements(callbackInfoMap));
423  CHECK_EQ(1, NumberOfElements(objectInfoMap));
424  CHECK_EQ(1, NumberOfElements(notifierObjectInfoMap));
426  CHECK_EQ(0, NumberOfElements(callbackInfoMap));
427  CHECK_EQ(0, NumberOfElements(objectInfoMap));
428  CHECK_EQ(0, NumberOfElements(notifierObjectInfoMap));
429 }
430 
431 
432 static bool NamedAccessAlwaysAllowed(Local<Object>, Local<Value>, AccessType,
433  Local<Value>) {
434  return true;
435 }
436 
437 
438 static bool IndexedAccessAlwaysAllowed(Local<Object>, uint32_t, AccessType,
439  Local<Value>) {
440  return true;
441 }
442 
443 
444 static AccessType g_access_block_type = ACCESS_GET;
445 static const uint32_t kBlockedContextIndex = 1337;
446 
447 
448 static bool NamedAccessAllowUnlessBlocked(Local<Object> host,
449  Local<Value> key,
450  AccessType type,
451  Local<Value> data) {
452  if (type != g_access_block_type) return true;
453  v8::Isolate* isolate = reinterpret_cast<v8::Isolate*>(
454  Utils::OpenHandle(*host)->GetIsolate());
455  Handle<Object> global = isolate->GetCurrentContext()->Global();
456  if (!global->Has(kBlockedContextIndex)) return true;
457  return !key->IsString() || !key->Equals(data);
458 }
459 
460 
461 static bool IndexedAccessAllowUnlessBlocked(Local<Object> host,
462  uint32_t index,
463  AccessType type,
464  Local<Value> data) {
465  if (type != g_access_block_type) return true;
466  v8::Isolate* isolate = reinterpret_cast<v8::Isolate*>(
467  Utils::OpenHandle(*host)->GetIsolate());
468  Handle<Object> global = isolate->GetCurrentContext()->Global();
469  if (!global->Has(kBlockedContextIndex)) return true;
470  return index != data->Uint32Value();
471 }
472 
473 
474 static bool BlockAccessKeys(Local<Object> host, Local<Value> key,
475  AccessType type, Local<Value>) {
476  v8::Isolate* isolate = reinterpret_cast<v8::Isolate*>(
477  Utils::OpenHandle(*host)->GetIsolate());
478  Handle<Object> global = isolate->GetCurrentContext()->Global();
479  return type != ACCESS_KEYS || !global->Has(kBlockedContextIndex);
480 }
481 
482 
483 static Handle<Object> CreateAccessCheckedObject(
484  v8::Isolate* isolate,
485  NamedSecurityCallback namedCallback,
486  IndexedSecurityCallback indexedCallback,
487  Handle<Value> data = Handle<Value>()) {
489  tmpl->SetAccessCheckCallbacks(namedCallback, indexedCallback, data);
490  Handle<Object> instance = tmpl->NewInstance();
491  Handle<Object> global = instance->CreationContext()->Global();
492  global->Set(String::NewFromUtf8(isolate, "obj"), instance);
493  global->Set(kBlockedContextIndex, v8::True(isolate));
494  return instance;
495 }
496 
497 
498 TEST(NamedAccessCheck) {
499  const AccessType types[] = { ACCESS_GET, ACCESS_HAS };
500  for (size_t i = 0; i < ARRAY_SIZE(types); ++i) {
501  HandleScope scope(CcTest::isolate());
502  LocalContext context(CcTest::isolate());
503  g_access_block_type = types[i];
504  Handle<Object> instance = CreateAccessCheckedObject(
505  CcTest::isolate(),
506  NamedAccessAllowUnlessBlocked,
507  IndexedAccessAlwaysAllowed,
509  CompileRun("var records = null;"
510  "var objNoCheck = {};"
511  "var observer = function(r) { records = r };"
512  "Object.observe(obj, observer);"
513  "Object.observe(objNoCheck, observer);");
514  Handle<Value> obj_no_check = CompileRun("objNoCheck");
515  {
516  LocalContext context2(CcTest::isolate());
517  context2->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "obj"),
518  instance);
519  context2->Global()->Set(
520  String::NewFromUtf8(CcTest::isolate(), "objNoCheck"),
521  obj_no_check);
522  CompileRun("var records2 = null;"
523  "var observer2 = function(r) { records2 = r };"
524  "Object.observe(obj, observer2);"
525  "Object.observe(objNoCheck, observer2);"
526  "obj.foo = 'bar';"
527  "Object.defineProperty(obj, 'foo', {value: 5});"
528  "Object.defineProperty(obj, 'foo', {get: function(){}});"
529  "obj.bar = 'baz';"
530  "objNoCheck.baz = 'quux'");
531  const RecordExpectation expected_records2[] = {
532  { instance, "add", "foo", Handle<Value>() },
533  { instance, "update", "foo",
535  { instance, "reconfigure", "foo",
537  { instance, "add", "bar", Handle<Value>() },
538  { obj_no_check, "add", "baz", Handle<Value>() },
539  };
540  EXPECT_RECORDS(CompileRun("records2"), expected_records2);
541  }
542  const RecordExpectation expected_records[] = {
543  { instance, "add", "bar", Handle<Value>() },
544  { obj_no_check, "add", "baz", Handle<Value>() }
545  };
546  EXPECT_RECORDS(CompileRun("records"), expected_records);
547  }
548 }
549 
550 
551 TEST(IndexedAccessCheck) {
552  const AccessType types[] = { ACCESS_GET, ACCESS_HAS };
553  for (size_t i = 0; i < ARRAY_SIZE(types); ++i) {
554  HandleScope scope(CcTest::isolate());
555  LocalContext context(CcTest::isolate());
556  g_access_block_type = types[i];
557  Handle<Object> instance = CreateAccessCheckedObject(
558  CcTest::isolate(), NamedAccessAlwaysAllowed,
559  IndexedAccessAllowUnlessBlocked, Number::New(CcTest::isolate(), 7));
560  CompileRun("var records = null;"
561  "var objNoCheck = {};"
562  "var observer = function(r) { records = r };"
563  "Object.observe(obj, observer);"
564  "Object.observe(objNoCheck, observer);");
565  Handle<Value> obj_no_check = CompileRun("objNoCheck");
566  {
567  LocalContext context2(CcTest::isolate());
568  context2->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "obj"),
569  instance);
570  context2->Global()->Set(
571  String::NewFromUtf8(CcTest::isolate(), "objNoCheck"),
572  obj_no_check);
573  CompileRun("var records2 = null;"
574  "var observer2 = function(r) { records2 = r };"
575  "Object.observe(obj, observer2);"
576  "Object.observe(objNoCheck, observer2);"
577  "obj[7] = 'foo';"
578  "Object.defineProperty(obj, '7', {value: 5});"
579  "Object.defineProperty(obj, '7', {get: function(){}});"
580  "obj[8] = 'bar';"
581  "objNoCheck[42] = 'quux'");
582  const RecordExpectation expected_records2[] = {
583  { instance, "add", "7", Handle<Value>() },
584  { instance, "update", "7",
586  { instance, "reconfigure", "7", Number::New(CcTest::isolate(), 5) },
587  { instance, "add", "8", Handle<Value>() },
588  { obj_no_check, "add", "42", Handle<Value>() }
589  };
590  EXPECT_RECORDS(CompileRun("records2"), expected_records2);
591  }
592  const RecordExpectation expected_records[] = {
593  { instance, "add", "8", Handle<Value>() },
594  { obj_no_check, "add", "42", Handle<Value>() }
595  };
596  EXPECT_RECORDS(CompileRun("records"), expected_records);
597  }
598 }
599 
600 
601 TEST(SpliceAccessCheck) {
602  HandleScope scope(CcTest::isolate());
603  LocalContext context(CcTest::isolate());
604  g_access_block_type = ACCESS_GET;
605  Handle<Object> instance = CreateAccessCheckedObject(
606  CcTest::isolate(), NamedAccessAlwaysAllowed,
607  IndexedAccessAllowUnlessBlocked, Number::New(CcTest::isolate(), 1));
608  CompileRun("var records = null;"
609  "obj[1] = 'foo';"
610  "obj.length = 2;"
611  "var objNoCheck = {1: 'bar', length: 2};"
612  "observer = function(r) { records = r };"
613  "Array.observe(obj, observer);"
614  "Array.observe(objNoCheck, observer);");
615  Handle<Value> obj_no_check = CompileRun("objNoCheck");
616  {
617  LocalContext context2(CcTest::isolate());
618  context2->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "obj"),
619  instance);
620  context2->Global()->Set(
621  String::NewFromUtf8(CcTest::isolate(), "objNoCheck"), obj_no_check);
622  CompileRun("var records2 = null;"
623  "var observer2 = function(r) { records2 = r };"
624  "Array.observe(obj, observer2);"
625  "Array.observe(objNoCheck, observer2);"
626  // No one should hear about this: no splice records are emitted
627  // for access-checked objects
628  "[].push.call(obj, 5);"
629  "[].splice.call(obj, 1, 1);"
630  "[].pop.call(obj);"
631  "[].pop.call(objNoCheck);");
632  // TODO(adamk): Extend EXPECT_RECORDS to be able to assert more things
633  // about splice records. For this test it's not so important since
634  // we just want to guarantee the machinery is in operation at all.
635  const RecordExpectation expected_records2[] = {
636  { obj_no_check, "splice", "", Handle<Value>() }
637  };
638  EXPECT_RECORDS(CompileRun("records2"), expected_records2);
639  }
640  const RecordExpectation expected_records[] = {
641  { obj_no_check, "splice", "", Handle<Value>() }
642  };
643  EXPECT_RECORDS(CompileRun("records"), expected_records);
644 }
645 
646 
647 TEST(DisallowAllForAccessKeys) {
648  HandleScope scope(CcTest::isolate());
649  LocalContext context(CcTest::isolate());
650  Handle<Object> instance = CreateAccessCheckedObject(
651  CcTest::isolate(), BlockAccessKeys, IndexedAccessAlwaysAllowed);
652  CompileRun("var records = null;"
653  "var objNoCheck = {};"
654  "var observer = function(r) { records = r };"
655  "Object.observe(obj, observer);"
656  "Object.observe(objNoCheck, observer);");
657  Handle<Value> obj_no_check = CompileRun("objNoCheck");
658  {
659  LocalContext context2(CcTest::isolate());
660  context2->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "obj"),
661  instance);
662  context2->Global()->Set(
663  String::NewFromUtf8(CcTest::isolate(), "objNoCheck"), obj_no_check);
664  CompileRun("var records2 = null;"
665  "var observer2 = function(r) { records2 = r };"
666  "Object.observe(obj, observer2);"
667  "Object.observe(objNoCheck, observer2);"
668  "obj.foo = 'bar';"
669  "obj[5] = 'baz';"
670  "objNoCheck.baz = 'quux'");
671  const RecordExpectation expected_records2[] = {
672  { instance, "add", "foo", Handle<Value>() },
673  { instance, "add", "5", Handle<Value>() },
674  { obj_no_check, "add", "baz", Handle<Value>() },
675  };
676  EXPECT_RECORDS(CompileRun("records2"), expected_records2);
677  }
678  const RecordExpectation expected_records[] = {
679  { obj_no_check, "add", "baz", Handle<Value>() }
680  };
681  EXPECT_RECORDS(CompileRun("records"), expected_records);
682 }
683 
684 
685 TEST(AccessCheckDisallowApiModifications) {
686  HandleScope scope(CcTest::isolate());
687  LocalContext context(CcTest::isolate());
688  Handle<Object> instance = CreateAccessCheckedObject(
689  CcTest::isolate(), BlockAccessKeys, IndexedAccessAlwaysAllowed);
690  CompileRun("var records = null;"
691  "var observer = function(r) { records = r };"
692  "Object.observe(obj, observer);");
693  {
694  LocalContext context2(CcTest::isolate());
695  context2->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "obj"),
696  instance);
697  CompileRun("var records2 = null;"
698  "var observer2 = function(r) { records2 = r };"
699  "Object.observe(obj, observer2);");
700  instance->Set(5, String::NewFromUtf8(CcTest::isolate(), "bar"));
701  instance->Set(String::NewFromUtf8(CcTest::isolate(), "foo"),
703  CompileRun(""); // trigger delivery
704  const RecordExpectation expected_records2[] = {
705  { instance, "add", "5", Handle<Value>() },
706  { instance, "add", "foo", Handle<Value>() }
707  };
708  EXPECT_RECORDS(CompileRun("records2"), expected_records2);
709  }
710  CHECK(CompileRun("records")->IsNull());
711 }
712 
713 
714 TEST(HiddenPropertiesLeakage) {
715  HandleScope scope(CcTest::isolate());
716  LocalContext context(CcTest::isolate());
717  CompileRun("var obj = {};"
718  "var records = null;"
719  "var observer = function(r) { records = r };"
720  "Object.observe(obj, observer);");
721  Handle<Value> obj =
722  context->Global()->Get(String::NewFromUtf8(CcTest::isolate(), "obj"));
724  ->SetHiddenValue(String::NewFromUtf8(CcTest::isolate(), "foo"),
725  Null(CcTest::isolate()));
726  CompileRun(""); // trigger delivery
727  CHECK(CompileRun("records")->IsNull());
728 }
enable upcoming ES6 features enable harmony block scoping enable harmony enable harmony proxies enable harmony generators enable harmony numeric enable harmony string enable harmony math functions harmony_scoping harmony_symbols harmony_collections harmony_iteration harmony_strings harmony_scoping harmony_maths tracks arrays with only smi values Optimize object Array DOM strings and string pretenure call new trace pretenuring decisions of HAllocate instructions track fields with only smi values track fields with heap values track_fields track_fields Enables optimizations which favor memory size over execution speed use string slices optimization filter maximum number of GVN fix point iterations use function inlining use allocation folding eliminate write barriers targeting allocations in optimized code maximum source size in bytes considered for a single inlining maximum cumulative number of AST nodes considered for inlining crankshaft harvests type feedback from stub cache trace check elimination phase hydrogen tracing filter NULL
#define CHECK_EQ(expected, value)
Definition: checks.h:252
void CollectAllGarbage(int flags, const char *gc_reason=NULL, const GCCallbackFlags gc_callback_flags=kNoGCCallbackFlags)
Definition: heap.cc:731
bool StrictEquals(Handle< Value > that) const
Definition: api.cc:2977
Local< Object > NewInstance()
Definition: api.cc:5284
static Handle< T > cast(Handle< S > that)
Definition: handles.h:75
bool(* IndexedSecurityCallback)(Local< Object > host, uint32_t index, AccessType type, Local< Value > data)
Definition: v8.h:3418
Local< ObjectTemplate > InstanceTemplate()
Definition: api.cc:1223
Local< Context > CreationContext()
Definition: api.cc:3639
Local< Context > GetCurrentContext()
Definition: api.cc:6359
#define CHECK(condition)
Definition: checks.h:75
Handle< Object > GetProperty(Handle< JSReceiver > obj, const char *name)
Definition: handles.cc:196
Factory * factory()
Definition: isolate.h:995
enable upcoming ES6 features enable harmony block scoping enable harmony enable harmony proxies enable harmony generators enable harmony numeric enable harmony string enable harmony math functions harmony_scoping harmony_symbols harmony_collections harmony_iteration harmony_strings harmony_scoping harmony_maths tracks arrays with only smi values Optimize object Array DOM strings and string pretenure call new trace pretenuring decisions of HAllocate instructions track fields with only smi values track fields with heap values track_fields track_fields Enables optimizations which favor memory size over execution speed use string slices optimization filter maximum number of GVN fix point iterations use function inlining use allocation folding eliminate write barriers targeting allocations in optimized code maximum source size in bytes considered for a single inlining maximum cumulative number of AST nodes considered for inlining crankshaft harvests type feedback from stub cache trace check elimination phase hydrogen tracing filter trace hydrogen to given file name trace inlining decisions trace store elimination trace all use positions trace global value numbering trace hydrogen escape analysis trace the tracking of allocation sites trace map generalization environment for every instruction deoptimize every n garbage collections put a break point before deoptimizing deoptimize uncommon cases use on stack replacement trace array bounds check elimination perform array index dehoisting use load elimination use store elimination use constant folding eliminate unreachable code number of stress runs when picking a function to watch for shared function not JSFunction itself flushes the cache of optimized code for closures on every GC functions with arguments object maximum number of escape analysis fix point iterations allow uint32 values on optimize frames if they are used only in safe operations track concurrent recompilation artificial compilation delay in ms concurrent on stack replacement do not emit check maps for constant values that have a leaf deoptimize the optimized code if the layout of the maps changes number of stack frames inspected by the profiler percentage of ICs that must have type info to allow optimization extra verbose compilation tracing generate extra emit comments in code disassembly enable use of SSE3 instructions if available enable use of CMOV instruction if available enable use of VFP3 instructions if available enable use of NEON instructions if enable use of SDIV and UDIV instructions if enable loading bit constant by means of movw movt instruction enable unaligned accesses for enable use of d16 d31 registers on ARM this requires VFP3 force all emitted branches to be in long expose natives in global object expose freeBuffer extension expose gc extension under the specified name expose externalize string extension number of stack frames to capture disable builtin natives files print name of functions for which code is generated use random jit cookie to mask large constants trace lazy optimization use adaptive optimizations always try to OSR functions trace optimize function deoptimization minimum length for automatic enable preparsing maximum number of optimization attempts before giving up cache prototype transitions trace debugging JSON request response trace out of bounds accesses to external arrays trace_js_array_abuse automatically set the debug break flag when debugger commands are in the queue abort by crashing maximum length of function source code printed in a stack trace max size of the new max size of the old max size of executable always perform global GCs print one trace line following each garbage collection do not print trace line after scavenger collection print statistics of the maximum memory committed for the heap in name
bool Equals(Handle< Value > that) const
Definition: api.cc:2950
V8_INLINE Handle< Boolean > True(Isolate *isolate)
Definition: v8.h:6559
static Local< ObjectTemplate > New()
Definition: api.cc:1286
void DetachGlobal()
Definition: api.cc:5253
bool ForceDelete(Handle< Value > key)
Definition: api.cc:3115
Handle< Value > old_value
enable upcoming ES6 features enable harmony block scoping enable harmony enable harmony proxies enable harmony generators enable harmony numeric enable harmony string enable harmony math functions harmony_scoping harmony_symbols harmony_collections harmony_iteration harmony_strings harmony_scoping harmony_maths tracks arrays with only smi values Optimize object Array DOM strings and string pretenure call new trace pretenuring decisions of HAllocate instructions track fields with only smi values track fields with heap values track_fields track_fields Enables optimizations which favor memory size over execution speed use string slices optimization filter maximum number of GVN fix point iterations use function inlining use allocation folding eliminate write barriers targeting allocations in optimized code maximum source size in bytes considered for a single inlining maximum cumulative number of AST nodes considered for inlining crankshaft harvests type feedback from stub cache trace check elimination phase hydrogen tracing filter trace hydrogen to given file name trace inlining decisions trace store elimination trace all use positions trace global value numbering trace hydrogen escape analysis trace the tracking of allocation sites trace map generalization environment for every instruction deoptimize every n garbage collections put a break point before deoptimizing deoptimize uncommon cases use on stack replacement trace array bounds check elimination perform array index dehoisting use load elimination use store elimination use constant folding eliminate unreachable code number of stress runs when picking a function to watch for shared function not JSFunction itself flushes the cache of optimized code for closures on every GC functions with arguments object maximum number of escape analysis fix point iterations allow uint32 values on optimize frames if they are used only in safe operations track concurrent recompilation artificial compilation delay in ms concurrent on stack replacement do not emit check maps for constant values that have a leaf map
static Local< FunctionTemplate > New(Isolate *isolate, FunctionCallback callback=0, Handle< Value > data=Handle< Value >(), Handle< Signature > signature=Handle< Signature >(), int length=0)
Definition: api.cc:942
static V8_INLINE Handle< T > Cast(Handle< S > that)
Definition: v8.h:297
Local< Object > Global()
Definition: api.cc:5239
TEST(PerIsolateState)
AccessType
Definition: v8.h:3395
static Local< Number > New(Isolate *isolate, double value)
Definition: api.cc:6220
bool SetPrototype(Handle< Value > prototype)
Definition: api.cc:3203
static v8::internal::Handle< To > OpenHandle(v8::Local< From > handle)
Definition: api.h:308
void SetHiddenPrototype(bool value)
Definition: api.cc:1257
static const int kAbortIncrementalMarkingMask
Definition: heap.h:1260
static Local< Object > New(Isolate *isolate)
Definition: api.cc:5589
Definition: v8.h:2472
V8_INLINE bool IsString() const
Definition: v8.h:6265
bool IsObject() const
Definition: api.cc:2411
uint32_t Uint32Value() const
Definition: api.cc:3023
static ObjectHashTable * cast(Object *obj)
Definition: objects.h:4226
Local< Function > GetFunction()
Definition: api.cc:5299
bool ForceSet(Handle< Value > key, Handle< Value > value, PropertyAttribute attribs=None)
Definition: api.cc:3087
bool(* NamedSecurityCallback)(Local< Object > host, Local< Value > key, AccessType type, Local< Value > data)
Definition: v8.h:3408
Handle< Value > object
bool Has(Handle< Value > key)
Definition: api.cc:3376
bool IsArray() const
Definition: api.cc:2374
HeapObject * obj
V8_INLINE Handle< S > As()
Definition: v8.h:306
bool Delete(Handle< Value > key)
Definition: api.cc:3356
#define EXPECT_RECORDS(records, expectations)
#define ARRAY_SIZE(a)
Definition: globals.h:333
V8_INLINE Handle< Primitive > Null(Isolate *isolate)
Definition: v8.h:6550
void SetAccessCheckCallbacks(NamedSecurityCallback named_handler, IndexedSecurityCallback indexed_handler, Handle< Value > data=Handle< Value >(), bool turned_on_by_default=true)
Definition: api.cc:1461
bool Set(Handle< Value > key, Handle< Value > value, PropertyAttribute attribs=None)
Definition: api.cc:3044
static v8::Isolate * isolate()
Definition: cctest.h:96
static Local< String > NewFromUtf8(Isolate *isolate, const char *data, NewStringType type=kNormalString, int length=-1)
Definition: api.cc:5417