The data contained in this repository can be downloaded to your computer using one of several clients.
Please see the documentation of your version control software client for more information.

Please select the desired protocol below to get the URL.

This URL has Read-Only access.

Statistics
| Branch: | Revision:

main_repo / deps / v8 / test / cctest / test-object-observe.cc @ f230a1cf

History | View | Annotate | Download (27.7 KB)

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
namespace {
36
// Need to create a new isolate when FLAG_harmony_observation is on.
37
class HarmonyIsolate {
38
 public:
39
  HarmonyIsolate() {
40
    i::FLAG_harmony_observation = true;
41
    isolate_ = Isolate::New();
42
    isolate_->Enter();
43
  }
44

    
45
  ~HarmonyIsolate() {
46
    isolate_->Exit();
47
    isolate_->Dispose();
48
  }
49

    
50
  Isolate* GetIsolate() const { return isolate_; }
51

    
52
 private:
53
  Isolate* isolate_;
54
};
55
}
56

    
57

    
58
TEST(PerIsolateState) {
59
  HarmonyIsolate isolate;
60
  HandleScope scope(isolate.GetIsolate());
61
  LocalContext context1(isolate.GetIsolate());
62
  CompileRun(
63
      "var count = 0;"
64
      "var calls = 0;"
65
      "var observer = function(records) { count = records.length; calls++ };"
66
      "var obj = {};"
67
      "Object.observe(obj, observer);");
68
  Handle<Value> observer = CompileRun("observer");
69
  Handle<Value> obj = CompileRun("obj");
70
  Handle<Value> notify_fun1 = CompileRun(
71
      "(function() { obj.foo = 'bar'; })");
72
  Handle<Value> notify_fun2;
73
  {
74
    LocalContext context2(isolate.GetIsolate());
75
    context2->Global()->Set(String::New("obj"), obj);
76
    notify_fun2 = CompileRun(
77
        "(function() { obj.foo = 'baz'; })");
78
  }
79
  Handle<Value> notify_fun3;
80
  {
81
    LocalContext context3(isolate.GetIsolate());
82
    context3->Global()->Set(String::New("obj"), obj);
83
    notify_fun3 = CompileRun(
84
        "(function() { obj.foo = 'bat'; })");
85
  }
86
  {
87
    LocalContext context4(isolate.GetIsolate());
88
    context4->Global()->Set(String::New("observer"), observer);
89
    context4->Global()->Set(String::New("fun1"), notify_fun1);
90
    context4->Global()->Set(String::New("fun2"), notify_fun2);
91
    context4->Global()->Set(String::New("fun3"), notify_fun3);
92
    CompileRun("fun1(); fun2(); fun3(); Object.deliverChangeRecords(observer)");
93
  }
94
  CHECK_EQ(1, CompileRun("calls")->Int32Value());
95
  CHECK_EQ(3, CompileRun("count")->Int32Value());
96
}
97

    
98

    
99
TEST(EndOfMicrotaskDelivery) {
100
  HarmonyIsolate isolate;
101
  HandleScope scope(isolate.GetIsolate());
102
  LocalContext context(isolate.GetIsolate());
103
  CompileRun(
104
      "var obj = {};"
105
      "var count = 0;"
106
      "var observer = function(records) { count = records.length };"
107
      "Object.observe(obj, observer);"
108
      "obj.foo = 'bar';");
109
  CHECK_EQ(1, CompileRun("count")->Int32Value());
110
}
111

    
112

    
113
TEST(DeliveryOrdering) {
114
  HarmonyIsolate isolate;
115
  HandleScope scope(isolate.GetIsolate());
116
  LocalContext context(isolate.GetIsolate());
117
  CompileRun(
118
      "var obj1 = {};"
119
      "var obj2 = {};"
120
      "var ordering = [];"
121
      "function observer2() { ordering.push(2); };"
122
      "function observer1() { ordering.push(1); };"
123
      "function observer3() { ordering.push(3); };"
124
      "Object.observe(obj1, observer1);"
125
      "Object.observe(obj1, observer2);"
126
      "Object.observe(obj1, observer3);"
127
      "obj1.foo = 'bar';");
128
  CHECK_EQ(3, CompileRun("ordering.length")->Int32Value());
129
  CHECK_EQ(1, CompileRun("ordering[0]")->Int32Value());
130
  CHECK_EQ(2, CompileRun("ordering[1]")->Int32Value());
131
  CHECK_EQ(3, CompileRun("ordering[2]")->Int32Value());
132
  CompileRun(
133
      "ordering = [];"
134
      "Object.observe(obj2, observer3);"
135
      "Object.observe(obj2, observer2);"
136
      "Object.observe(obj2, observer1);"
137
      "obj2.foo = 'baz'");
138
  CHECK_EQ(3, CompileRun("ordering.length")->Int32Value());
139
  CHECK_EQ(1, CompileRun("ordering[0]")->Int32Value());
140
  CHECK_EQ(2, CompileRun("ordering[1]")->Int32Value());
141
  CHECK_EQ(3, CompileRun("ordering[2]")->Int32Value());
142
}
143

    
144

    
145
TEST(DeliveryOrderingReentrant) {
146
  HarmonyIsolate isolate;
147
  HandleScope scope(isolate.GetIsolate());
148
  LocalContext context(isolate.GetIsolate());
149
  CompileRun(
150
      "var obj = {};"
151
      "var reentered = false;"
152
      "var ordering = [];"
153
      "function observer1() { ordering.push(1); };"
154
      "function observer2() {"
155
      "  if (!reentered) {"
156
      "    obj.foo = 'baz';"
157
      "    reentered = true;"
158
      "  }"
159
      "  ordering.push(2);"
160
      "};"
161
      "function observer3() { ordering.push(3); };"
162
      "Object.observe(obj, observer1);"
163
      "Object.observe(obj, observer2);"
164
      "Object.observe(obj, observer3);"
165
      "obj.foo = 'bar';");
166
  CHECK_EQ(5, CompileRun("ordering.length")->Int32Value());
167
  CHECK_EQ(1, CompileRun("ordering[0]")->Int32Value());
168
  CHECK_EQ(2, CompileRun("ordering[1]")->Int32Value());
169
  CHECK_EQ(3, CompileRun("ordering[2]")->Int32Value());
170
  // Note that we re-deliver to observers 1 and 2, while observer3
171
  // already received the second record during the first round.
172
  CHECK_EQ(1, CompileRun("ordering[3]")->Int32Value());
173
  CHECK_EQ(2, CompileRun("ordering[1]")->Int32Value());
174
}
175

    
176

    
177
TEST(DeliveryOrderingDeliverChangeRecords) {
178
  HarmonyIsolate isolate;
179
  HandleScope scope(isolate.GetIsolate());
180
  LocalContext context(isolate.GetIsolate());
181
  CompileRun(
182
      "var obj = {};"
183
      "var ordering = [];"
184
      "function observer1() { ordering.push(1); if (!obj.b) obj.b = true };"
185
      "function observer2() { ordering.push(2); };"
186
      "Object.observe(obj, observer1);"
187
      "Object.observe(obj, observer2);"
188
      "obj.a = 1;"
189
      "Object.deliverChangeRecords(observer2);");
190
  CHECK_EQ(4, CompileRun("ordering.length")->Int32Value());
191
  // First, observer2 is called due to deliverChangeRecords
192
  CHECK_EQ(2, CompileRun("ordering[0]")->Int32Value());
193
  // Then, observer1 is called when the stack unwinds
194
  CHECK_EQ(1, CompileRun("ordering[1]")->Int32Value());
195
  // observer1's mutation causes both 1 and 2 to be reactivated,
196
  // with 1 having priority.
197
  CHECK_EQ(1, CompileRun("ordering[2]")->Int32Value());
198
  CHECK_EQ(2, CompileRun("ordering[3]")->Int32Value());
199
}
200

    
201

    
202
TEST(ObjectHashTableGrowth) {
203
  HarmonyIsolate isolate;
204
  HandleScope scope(isolate.GetIsolate());
205
  // Initializing this context sets up initial hash tables.
206
  LocalContext context(isolate.GetIsolate());
207
  Handle<Value> obj = CompileRun("obj = {};");
208
  Handle<Value> observer = CompileRun(
209
      "var ran = false;"
210
      "(function() { ran = true })");
211
  {
212
    // As does initializing this context.
213
    LocalContext context2(isolate.GetIsolate());
214
    context2->Global()->Set(String::New("obj"), obj);
215
    context2->Global()->Set(String::New("observer"), observer);
216
    CompileRun(
217
        "var objArr = [];"
218
        // 100 objects should be enough to make the hash table grow
219
        // (and thus relocate).
220
        "for (var i = 0; i < 100; ++i) {"
221
        "  objArr.push({});"
222
        "  Object.observe(objArr[objArr.length-1], function(){});"
223
        "}"
224
        "Object.observe(obj, observer);");
225
  }
226
  // obj is now marked "is_observed", but our map has moved.
227
  CompileRun("obj.foo = 'bar'");
228
  CHECK(CompileRun("ran")->BooleanValue());
229
}
230

    
231

    
232
TEST(GlobalObjectObservation) {
233
  HarmonyIsolate isolate;
234
  LocalContext context(isolate.GetIsolate());
235
  HandleScope scope(isolate.GetIsolate());
236
  Handle<Object> global_proxy = context->Global();
237
  Handle<Object> inner_global = global_proxy->GetPrototype().As<Object>();
238
  CompileRun(
239
      "var records = [];"
240
      "var global = this;"
241
      "Object.observe(global, function(r) { [].push.apply(records, r) });"
242
      "global.foo = 'hello';");
243
  CHECK_EQ(1, CompileRun("records.length")->Int32Value());
244
  CHECK(global_proxy->StrictEquals(CompileRun("records[0].object")));
245

    
246
  // Detached, mutating the proxy has no effect.
247
  context->DetachGlobal();
248
  CompileRun("global.bar = 'goodbye';");
249
  CHECK_EQ(1, CompileRun("records.length")->Int32Value());
250

    
251
  // Mutating the global object directly still has an effect...
252
  CompileRun("this.bar = 'goodbye';");
253
  CHECK_EQ(2, CompileRun("records.length")->Int32Value());
254
  CHECK(inner_global->StrictEquals(CompileRun("records[1].object")));
255

    
256
  // Reattached, back to global proxy.
257
  context->ReattachGlobal(global_proxy);
258
  CompileRun("global.baz = 'again';");
259
  CHECK_EQ(3, CompileRun("records.length")->Int32Value());
260
  CHECK(global_proxy->StrictEquals(CompileRun("records[2].object")));
261

    
262
  // Attached to a different context, should not leak mutations
263
  // to the old context.
264
  context->DetachGlobal();
265
  {
266
    LocalContext context2(isolate.GetIsolate());
267
    context2->DetachGlobal();
268
    context2->ReattachGlobal(global_proxy);
269
    CompileRun(
270
        "var records2 = [];"
271
        "Object.observe(this, function(r) { [].push.apply(records2, r) });"
272
        "this.bat = 'context2';");
273
    CHECK_EQ(1, CompileRun("records2.length")->Int32Value());
274
    CHECK(global_proxy->StrictEquals(CompileRun("records2[0].object")));
275
  }
276
  CHECK_EQ(3, CompileRun("records.length")->Int32Value());
277

    
278
  // Attaching by passing to Context::New
279
  {
280
    // Delegates to Context::New
281
    LocalContext context3(
282
        isolate.GetIsolate(), NULL, Handle<ObjectTemplate>(), global_proxy);
283
    CompileRun(
284
        "var records3 = [];"
285
        "Object.observe(this, function(r) { [].push.apply(records3, r) });"
286
        "this.qux = 'context3';");
287
    CHECK_EQ(1, CompileRun("records3.length")->Int32Value());
288
    CHECK(global_proxy->StrictEquals(CompileRun("records3[0].object")));
289
  }
290
  CHECK_EQ(3, CompileRun("records.length")->Int32Value());
291
}
292

    
293

    
294
struct RecordExpectation {
295
  Handle<Value> object;
296
  const char* type;
297
  const char* name;
298
  Handle<Value> old_value;
299
};
300

    
301

    
302
// TODO(adamk): Use this helper elsewhere in this file.
303
static void ExpectRecords(Handle<Value> records,
304
                          const RecordExpectation expectations[],
305
                          int num) {
306
  CHECK(records->IsArray());
307
  Handle<Array> recordArray = records.As<Array>();
308
  CHECK_EQ(num, static_cast<int>(recordArray->Length()));
309
  for (int i = 0; i < num; ++i) {
310
    Handle<Value> record = recordArray->Get(i);
311
    CHECK(record->IsObject());
312
    Handle<Object> recordObj = record.As<Object>();
313
    CHECK(expectations[i].object->StrictEquals(
314
        recordObj->Get(String::New("object"))));
315
    CHECK(String::New(expectations[i].type)->Equals(
316
        recordObj->Get(String::New("type"))));
317
    if (strcmp("splice", expectations[i].type) != 0) {
318
      CHECK(String::New(expectations[i].name)->Equals(
319
          recordObj->Get(String::New("name"))));
320
      if (!expectations[i].old_value.IsEmpty()) {
321
        CHECK(expectations[i].old_value->Equals(
322
            recordObj->Get(String::New("oldValue"))));
323
      }
324
    }
325
  }
326
}
327

    
328
#define EXPECT_RECORDS(records, expectations) \
329
    ExpectRecords(records, expectations, ARRAY_SIZE(expectations))
330

    
331
TEST(APITestBasicMutation) {
332
  HarmonyIsolate isolate;
333
  HandleScope scope(isolate.GetIsolate());
334
  LocalContext context(isolate.GetIsolate());
335
  Handle<Object> obj = Handle<Object>::Cast(CompileRun(
336
      "var records = [];"
337
      "var obj = {};"
338
      "function observer(r) { [].push.apply(records, r); };"
339
      "Object.observe(obj, observer);"
340
      "obj"));
341
  obj->Set(String::New("foo"), Number::New(7));
342
  obj->Set(1, Number::New(2));
343
  // ForceSet should work just as well as Set
344
  obj->ForceSet(String::New("foo"), Number::New(3));
345
  obj->ForceSet(Number::New(1), Number::New(4));
346
  // Setting an indexed element via the property setting method
347
  obj->Set(Number::New(1), Number::New(5));
348
  // Setting with a non-String, non-uint32 key
349
  obj->Set(Number::New(1.1), Number::New(6), DontDelete);
350
  obj->Delete(String::New("foo"));
351
  obj->Delete(1);
352
  obj->ForceDelete(Number::New(1.1));
353

    
354
  // Force delivery
355
  // TODO(adamk): Should the above set methods trigger delivery themselves?
356
  CompileRun("void 0");
357
  CHECK_EQ(9, CompileRun("records.length")->Int32Value());
358
  const RecordExpectation expected_records[] = {
359
    { obj, "new", "foo", Handle<Value>() },
360
    { obj, "new", "1", Handle<Value>() },
361
    // Note: use 7 not 1 below, as the latter triggers a nifty VS10 compiler bug
362
    // where instead of 1.0, a garbage value would be passed into Number::New.
363
    { obj, "updated", "foo", Number::New(7) },
364
    { obj, "updated", "1", Number::New(2) },
365
    { obj, "updated", "1", Number::New(4) },
366
    { obj, "new", "1.1", Handle<Value>() },
367
    { obj, "deleted", "foo", Number::New(3) },
368
    { obj, "deleted", "1", Number::New(5) },
369
    { obj, "deleted", "1.1", Number::New(6) }
370
  };
371
  EXPECT_RECORDS(CompileRun("records"), expected_records);
372
}
373

    
374

    
375
TEST(HiddenPrototypeObservation) {
376
  HarmonyIsolate isolate;
377
  HandleScope scope(isolate.GetIsolate());
378
  LocalContext context(isolate.GetIsolate());
379
  Handle<FunctionTemplate> tmpl = FunctionTemplate::New();
380
  tmpl->SetHiddenPrototype(true);
381
  tmpl->InstanceTemplate()->Set(String::New("foo"), Number::New(75));
382
  Handle<Object> proto = tmpl->GetFunction()->NewInstance();
383
  Handle<Object> obj = Object::New();
384
  obj->SetPrototype(proto);
385
  context->Global()->Set(String::New("obj"), obj);
386
  context->Global()->Set(String::New("proto"), proto);
387
  CompileRun(
388
      "var records;"
389
      "function observer(r) { records = r; };"
390
      "Object.observe(obj, observer);"
391
      "obj.foo = 41;"  // triggers a notification
392
      "proto.foo = 42;");  // does not trigger a notification
393
  const RecordExpectation expected_records[] = {
394
    { obj, "updated", "foo", Number::New(75) }
395
  };
396
  EXPECT_RECORDS(CompileRun("records"), expected_records);
397
  obj->SetPrototype(Null(isolate.GetIsolate()));
398
  CompileRun("obj.foo = 43");
399
  const RecordExpectation expected_records2[] = {
400
    { obj, "new", "foo", Handle<Value>() }
401
  };
402
  EXPECT_RECORDS(CompileRun("records"), expected_records2);
403
  obj->SetPrototype(proto);
404
  CompileRun(
405
      "Object.observe(proto, observer);"
406
      "proto.bar = 1;"
407
      "Object.unobserve(obj, observer);"
408
      "obj.foo = 44;");
409
  const RecordExpectation expected_records3[] = {
410
    { proto, "new", "bar", Handle<Value>() }
411
    // TODO(adamk): The below record should be emitted since proto is observed
412
    // and has been modified. Not clear if this happens in practice.
413
    // { proto, "updated", "foo", Number::New(43) }
414
  };
415
  EXPECT_RECORDS(CompileRun("records"), expected_records3);
416
}
417

    
418

    
419
static int NumberOfElements(i::Handle<i::JSWeakMap> map) {
420
  return i::ObjectHashTable::cast(map->table())->NumberOfElements();
421
}
422

    
423

    
424
TEST(ObservationWeakMap) {
425
  HarmonyIsolate isolate;
426
  HandleScope scope(isolate.GetIsolate());
427
  LocalContext context(isolate.GetIsolate());
428
  CompileRun(
429
      "var obj = {};"
430
      "Object.observe(obj, function(){});"
431
      "Object.getNotifier(obj);"
432
      "obj = null;");
433
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate.GetIsolate());
434
  i::Handle<i::JSObject> observation_state =
435
      i_isolate->factory()->observation_state();
436
  i::Handle<i::JSWeakMap> callbackInfoMap =
437
      i::Handle<i::JSWeakMap>::cast(
438
          i::GetProperty(observation_state, "callbackInfoMap"));
439
  i::Handle<i::JSWeakMap> objectInfoMap =
440
      i::Handle<i::JSWeakMap>::cast(
441
          i::GetProperty(observation_state, "objectInfoMap"));
442
  i::Handle<i::JSWeakMap> notifierObjectInfoMap =
443
      i::Handle<i::JSWeakMap>::cast(
444
          i::GetProperty(observation_state, "notifierObjectInfoMap"));
445
  CHECK_EQ(1, NumberOfElements(callbackInfoMap));
446
  CHECK_EQ(1, NumberOfElements(objectInfoMap));
447
  CHECK_EQ(1, NumberOfElements(notifierObjectInfoMap));
448
  i_isolate->heap()->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask);
449
  CHECK_EQ(0, NumberOfElements(callbackInfoMap));
450
  CHECK_EQ(0, NumberOfElements(objectInfoMap));
451
  CHECK_EQ(0, NumberOfElements(notifierObjectInfoMap));
452
}
453

    
454

    
455
static bool NamedAccessAlwaysAllowed(Local<Object>, Local<Value>, AccessType,
456
                                     Local<Value>) {
457
  return true;
458
}
459

    
460

    
461
static bool IndexedAccessAlwaysAllowed(Local<Object>, uint32_t, AccessType,
462
                                       Local<Value>) {
463
  return true;
464
}
465

    
466

    
467
static AccessType g_access_block_type = ACCESS_GET;
468
static const uint32_t kBlockedContextIndex = 1337;
469

    
470

    
471
static bool NamedAccessAllowUnlessBlocked(Local<Object> host,
472
                                          Local<Value> key,
473
                                          AccessType type,
474
                                          Local<Value> data) {
475
  if (type != g_access_block_type) return true;
476
  v8::Isolate* isolate = reinterpret_cast<v8::Isolate*>(
477
      Utils::OpenHandle(*host)->GetIsolate());
478
  Handle<Object> global = isolate->GetCurrentContext()->Global();
479
  if (!global->Has(kBlockedContextIndex)) return true;
480
  return !key->IsString() || !key->Equals(data);
481
}
482

    
483

    
484
static bool IndexedAccessAllowUnlessBlocked(Local<Object> host,
485
                                            uint32_t index,
486
                                            AccessType type,
487
                                            Local<Value> data) {
488
  if (type != g_access_block_type) return true;
489
  v8::Isolate* isolate = reinterpret_cast<v8::Isolate*>(
490
      Utils::OpenHandle(*host)->GetIsolate());
491
  Handle<Object> global = isolate->GetCurrentContext()->Global();
492
  if (!global->Has(kBlockedContextIndex)) return true;
493
  return index != data->Uint32Value();
494
}
495

    
496

    
497
static bool BlockAccessKeys(Local<Object> host, Local<Value> key,
498
                            AccessType type, Local<Value>) {
499
  v8::Isolate* isolate = reinterpret_cast<v8::Isolate*>(
500
      Utils::OpenHandle(*host)->GetIsolate());
501
  Handle<Object> global = isolate->GetCurrentContext()->Global();
502
  return type != ACCESS_KEYS || !global->Has(kBlockedContextIndex);
503
}
504

    
505

    
506
static Handle<Object> CreateAccessCheckedObject(
507
    NamedSecurityCallback namedCallback,
508
    IndexedSecurityCallback indexedCallback,
509
    Handle<Value> data = Handle<Value>()) {
510
  Handle<ObjectTemplate> tmpl = ObjectTemplate::New();
511
  tmpl->SetAccessCheckCallbacks(namedCallback, indexedCallback, data);
512
  Handle<Object> instance = tmpl->NewInstance();
513
  Handle<Object> global = instance->CreationContext()->Global();
514
  global->Set(String::New("obj"), instance);
515
  global->Set(kBlockedContextIndex, v8::True());
516
  return instance;
517
}
518

    
519

    
520
TEST(NamedAccessCheck) {
521
  HarmonyIsolate isolate;
522
  const AccessType types[] = { ACCESS_GET, ACCESS_HAS };
523
  for (size_t i = 0; i < ARRAY_SIZE(types); ++i) {
524
    HandleScope scope(isolate.GetIsolate());
525
    LocalContext context(isolate.GetIsolate());
526
    g_access_block_type = types[i];
527
    Handle<Object> instance = CreateAccessCheckedObject(
528
        NamedAccessAllowUnlessBlocked,
529
        IndexedAccessAlwaysAllowed,
530
        String::New("foo"));
531
    CompileRun("var records = null;"
532
               "var objNoCheck = {};"
533
               "var observer = function(r) { records = r };"
534
               "Object.observe(obj, observer);"
535
               "Object.observe(objNoCheck, observer);");
536
    Handle<Value> obj_no_check = CompileRun("objNoCheck");
537
    {
538
      LocalContext context2(isolate.GetIsolate());
539
      context2->Global()->Set(String::New("obj"), instance);
540
      context2->Global()->Set(String::New("objNoCheck"), obj_no_check);
541
      CompileRun("var records2 = null;"
542
                 "var observer2 = function(r) { records2 = r };"
543
                 "Object.observe(obj, observer2);"
544
                 "Object.observe(objNoCheck, observer2);"
545
                 "obj.foo = 'bar';"
546
                 "Object.defineProperty(obj, 'foo', {value: 5});"
547
                 "Object.defineProperty(obj, 'foo', {get: function(){}});"
548
                 "obj.bar = 'baz';"
549
                 "objNoCheck.baz = 'quux'");
550
      const RecordExpectation expected_records2[] = {
551
        { instance, "new", "foo", Handle<Value>() },
552
        { instance, "updated", "foo", String::New("bar") },
553
        { instance, "reconfigured", "foo", Number::New(5) },
554
        { instance, "new", "bar", Handle<Value>() },
555
        { obj_no_check, "new", "baz", Handle<Value>() },
556
      };
557
      EXPECT_RECORDS(CompileRun("records2"), expected_records2);
558
    }
559
    const RecordExpectation expected_records[] = {
560
      { instance, "new", "bar", Handle<Value>() },
561
      { obj_no_check, "new", "baz", Handle<Value>() }
562
    };
563
    EXPECT_RECORDS(CompileRun("records"), expected_records);
564
  }
565
}
566

    
567

    
568
TEST(IndexedAccessCheck) {
569
  HarmonyIsolate isolate;
570
  const AccessType types[] = { ACCESS_GET, ACCESS_HAS };
571
  for (size_t i = 0; i < ARRAY_SIZE(types); ++i) {
572
    HandleScope scope(isolate.GetIsolate());
573
    LocalContext context(isolate.GetIsolate());
574
    g_access_block_type = types[i];
575
    Handle<Object> instance = CreateAccessCheckedObject(
576
        NamedAccessAlwaysAllowed, IndexedAccessAllowUnlessBlocked,
577
        Number::New(7));
578
    CompileRun("var records = null;"
579
               "var objNoCheck = {};"
580
               "var observer = function(r) { records = r };"
581
               "Object.observe(obj, observer);"
582
               "Object.observe(objNoCheck, observer);");
583
    Handle<Value> obj_no_check = CompileRun("objNoCheck");
584
    {
585
      LocalContext context2(isolate.GetIsolate());
586
      context2->Global()->Set(String::New("obj"), instance);
587
      context2->Global()->Set(String::New("objNoCheck"), obj_no_check);
588
      CompileRun("var records2 = null;"
589
                 "var observer2 = function(r) { records2 = r };"
590
                 "Object.observe(obj, observer2);"
591
                 "Object.observe(objNoCheck, observer2);"
592
                 "obj[7] = 'foo';"
593
                 "Object.defineProperty(obj, '7', {value: 5});"
594
                 "Object.defineProperty(obj, '7', {get: function(){}});"
595
                 "obj[8] = 'bar';"
596
                 "objNoCheck[42] = 'quux'");
597
      const RecordExpectation expected_records2[] = {
598
        { instance, "new", "7", Handle<Value>() },
599
        { instance, "updated", "7", String::New("foo") },
600
        { instance, "reconfigured", "7", Number::New(5) },
601
        { instance, "new", "8", Handle<Value>() },
602
        { obj_no_check, "new", "42", Handle<Value>() }
603
      };
604
      EXPECT_RECORDS(CompileRun("records2"), expected_records2);
605
    }
606
    const RecordExpectation expected_records[] = {
607
      { instance, "new", "8", Handle<Value>() },
608
      { obj_no_check, "new", "42", Handle<Value>() }
609
    };
610
    EXPECT_RECORDS(CompileRun("records"), expected_records);
611
  }
612
}
613

    
614

    
615
TEST(SpliceAccessCheck) {
616
  HarmonyIsolate isolate;
617
  HandleScope scope(isolate.GetIsolate());
618
  LocalContext context(isolate.GetIsolate());
619
  g_access_block_type = ACCESS_GET;
620
  Handle<Object> instance = CreateAccessCheckedObject(
621
      NamedAccessAlwaysAllowed, IndexedAccessAllowUnlessBlocked,
622
      Number::New(1));
623
  CompileRun("var records = null;"
624
             "obj[1] = 'foo';"
625
             "obj.length = 2;"
626
             "var objNoCheck = {1: 'bar', length: 2};"
627
             "observer = function(r) { records = r };"
628
             "Array.observe(obj, observer);"
629
             "Array.observe(objNoCheck, observer);");
630
  Handle<Value> obj_no_check = CompileRun("objNoCheck");
631
  {
632
    LocalContext context2(isolate.GetIsolate());
633
    context2->Global()->Set(String::New("obj"), instance);
634
    context2->Global()->Set(String::New("objNoCheck"), obj_no_check);
635
    CompileRun("var records2 = null;"
636
               "var observer2 = function(r) { records2 = r };"
637
               "Array.observe(obj, observer2);"
638
               "Array.observe(objNoCheck, observer2);"
639
               // No one should hear about this: no splice records are emitted
640
               // for access-checked objects
641
               "[].push.call(obj, 5);"
642
               "[].splice.call(obj, 1, 1);"
643
               "[].pop.call(obj);"
644
               "[].pop.call(objNoCheck);");
645
    // TODO(adamk): Extend EXPECT_RECORDS to be able to assert more things
646
    // about splice records. For this test it's not so important since
647
    // we just want to guarantee the machinery is in operation at all.
648
    const RecordExpectation expected_records2[] = {
649
      { obj_no_check, "splice", "", Handle<Value>() }
650
    };
651
    EXPECT_RECORDS(CompileRun("records2"), expected_records2);
652
  }
653
  const RecordExpectation expected_records[] = {
654
    { obj_no_check, "splice", "", Handle<Value>() }
655
  };
656
  EXPECT_RECORDS(CompileRun("records"), expected_records);
657
}
658

    
659

    
660
TEST(DisallowAllForAccessKeys) {
661
  HarmonyIsolate isolate;
662
  HandleScope scope(isolate.GetIsolate());
663
  LocalContext context(isolate.GetIsolate());
664
  Handle<Object> instance = CreateAccessCheckedObject(
665
      BlockAccessKeys, IndexedAccessAlwaysAllowed);
666
  CompileRun("var records = null;"
667
             "var objNoCheck = {};"
668
             "var observer = function(r) { records = r };"
669
             "Object.observe(obj, observer);"
670
             "Object.observe(objNoCheck, observer);");
671
  Handle<Value> obj_no_check = CompileRun("objNoCheck");
672
  {
673
    LocalContext context2(isolate.GetIsolate());
674
    context2->Global()->Set(String::New("obj"), instance);
675
    context2->Global()->Set(String::New("objNoCheck"), obj_no_check);
676
    CompileRun("var records2 = null;"
677
               "var observer2 = function(r) { records2 = r };"
678
               "Object.observe(obj, observer2);"
679
               "Object.observe(objNoCheck, observer2);"
680
               "obj.foo = 'bar';"
681
               "obj[5] = 'baz';"
682
               "objNoCheck.baz = 'quux'");
683
    const RecordExpectation expected_records2[] = {
684
      { instance, "new", "foo", Handle<Value>() },
685
      { instance, "new", "5", Handle<Value>() },
686
      { obj_no_check, "new", "baz", Handle<Value>() },
687
    };
688
    EXPECT_RECORDS(CompileRun("records2"), expected_records2);
689
  }
690
  const RecordExpectation expected_records[] = {
691
    { obj_no_check, "new", "baz", Handle<Value>() }
692
  };
693
  EXPECT_RECORDS(CompileRun("records"), expected_records);
694
}
695

    
696

    
697
TEST(AccessCheckDisallowApiModifications) {
698
  HarmonyIsolate isolate;
699
  HandleScope scope(isolate.GetIsolate());
700
  LocalContext context(isolate.GetIsolate());
701
  Handle<Object> instance = CreateAccessCheckedObject(
702
      BlockAccessKeys, IndexedAccessAlwaysAllowed);
703
  CompileRun("var records = null;"
704
             "var observer = function(r) { records = r };"
705
             "Object.observe(obj, observer);");
706
  {
707
    LocalContext context2(isolate.GetIsolate());
708
    context2->Global()->Set(String::New("obj"), instance);
709
    CompileRun("var records2 = null;"
710
               "var observer2 = function(r) { records2 = r };"
711
               "Object.observe(obj, observer2);");
712
    instance->Set(5, String::New("bar"));
713
    instance->Set(String::New("foo"), String::New("bar"));
714
    CompileRun("");  // trigger delivery
715
    const RecordExpectation expected_records2[] = {
716
      { instance, "new", "5", Handle<Value>() },
717
      { instance, "new", "foo", Handle<Value>() }
718
    };
719
    EXPECT_RECORDS(CompileRun("records2"), expected_records2);
720
  }
721
  CHECK(CompileRun("records")->IsNull());
722
}
723

    
724

    
725
TEST(HiddenPropertiesLeakage) {
726
  HarmonyIsolate isolate;
727
  HandleScope scope(isolate.GetIsolate());
728
  LocalContext context(isolate.GetIsolate());
729
  CompileRun("var obj = {};"
730
             "var records = null;"
731
             "var observer = function(r) { records = r };"
732
             "Object.observe(obj, observer);");
733
  Handle<Value> obj = context->Global()->Get(String::New("obj"));
734
  Handle<Object>::Cast(obj)->SetHiddenValue(String::New("foo"), Null());
735
  CompileRun("");  // trigger delivery
736
  CHECK(CompileRun("records")->IsNull());
737
}