summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/basic/shape-teleporting-invalidation.js
blob: 37769bcb5a467151020a33d1f8e3a594eddbcbed (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// The shape teleporting optimization can be disabled for an object that's used
// as prototype when either it's involved in prototype changes or it had a property
// shadowed on another prototype object.

function changeProps(o) {
  Object.assign(o, {x: 1, y: 2, z: 3});
  o.foo = 4;
  delete o.x;
}

function testProtoChange() {
  var receiver = {};
  var A = Object.create(null);
  var B = Object.create(A);

  // Change |receiver|'s proto: receiver => B => A => null
  // Because |receiver| is not used as a prototype object, this doesn't affect
  // teleporting.
  Object.setPrototypeOf(receiver, B);
  assertEq(hasInvalidatedTeleporting(receiver), false);
  assertEq(hasInvalidatedTeleporting(A), false);
  assertEq(hasInvalidatedTeleporting(B), false);

  // Change B's proto to C: receiver => B => C => null
  // Because B is used as prototype object, both A and B invalidate teleporting.
  var C = Object.create(null);
  Object.setPrototypeOf(B, C);
  assertEq(hasInvalidatedTeleporting(receiver), false);
  assertEq(hasInvalidatedTeleporting(A), true);
  assertEq(hasInvalidatedTeleporting(B), true);
  assertEq(hasInvalidatedTeleporting(C), false);

  // Change B's proto a second time: receiver => B => D => null
  // Now C has teleporting invalidated too.
  var D = Object.create(null);
  Object.setPrototypeOf(B, D);
  assertEq(hasInvalidatedTeleporting(receiver), false);
  assertEq(hasInvalidatedTeleporting(A), true);
  assertEq(hasInvalidatedTeleporting(B), true);
  assertEq(hasInvalidatedTeleporting(C), true);
  assertEq(hasInvalidatedTeleporting(D), false);

  // Changing properties (without shadowing) must not affect teleporting state.
  changeProps(C);
  changeProps(D);
  assertEq(hasInvalidatedTeleporting(C), true);
  assertEq(hasInvalidatedTeleporting(D), false);
}
testProtoChange();

function testShadowingProp() {
  // receiver => C => B => A => null
  var A = Object.create(null);
  var B = Object.create(A);
  var C = Object.create(B);
  var receiver = Object.create(C);

  // Adding non-shadowing properties doesn't affect teleporting.
  A.a = 1;
  B.b = 1;
  C.c = 1;
  receiver.receiver = 1;
  assertEq(hasInvalidatedTeleporting(receiver), false);
  assertEq(hasInvalidatedTeleporting(C), false);
  assertEq(hasInvalidatedTeleporting(B), false);
  assertEq(hasInvalidatedTeleporting(A), false);

  // Objects not used as prototype can shadow properties without affecting
  // teleporting.
  receiver.a = 1;
  receiver.b = 2;
  receiver.c = 3;
  assertEq(hasInvalidatedTeleporting(receiver), false);
  assertEq(hasInvalidatedTeleporting(C), false);
  assertEq(hasInvalidatedTeleporting(B), false);
  assertEq(hasInvalidatedTeleporting(A), false);

  // Shadowing a property of B on C invalidates teleporting for B.
  C.b = 1;
  assertEq(hasInvalidatedTeleporting(receiver), false);
  assertEq(hasInvalidatedTeleporting(C), false);
  assertEq(hasInvalidatedTeleporting(B), true);
  assertEq(hasInvalidatedTeleporting(A), false);

  // Shadowing a property of A on C invalidates teleporting for A.
  C.a = 2;
  assertEq(hasInvalidatedTeleporting(receiver), false);
  assertEq(hasInvalidatedTeleporting(C), false);
  assertEq(hasInvalidatedTeleporting(B), true);
  assertEq(hasInvalidatedTeleporting(A), true);

  // Changing properties (without shadowing) must not affect teleporting state.
  changeProps(C);
  changeProps(B);
  assertEq(hasInvalidatedTeleporting(C), false);
  assertEq(hasInvalidatedTeleporting(B), true);
}
testShadowingProp();

function testShadowingPropStopsAtFirst() {
  // receiver => C => B{x,y} => A{x,y,z} => null
  var A = Object.create(null);
  A.x = 1;
  A.y = 2;
  A.z = 3;
  var B = Object.create(A);
  B.x = 1;
  B.y = 2;
  var C = Object.create(B);
  var receiver = Object.create(C);

  // Teleporting is supported.
  assertEq(hasInvalidatedTeleporting(receiver), false);
  assertEq(hasInvalidatedTeleporting(C), false);
  assertEq(hasInvalidatedTeleporting(B), false);
  assertEq(hasInvalidatedTeleporting(A), false);

  // Shadowing a property of B (and A) on C.
  // This invalidates teleporting for B but not for A, because the search stops
  // at B.
  C.x = 1;
  assertEq(hasInvalidatedTeleporting(receiver), false);
  assertEq(hasInvalidatedTeleporting(C), false);
  assertEq(hasInvalidatedTeleporting(B), true);
  assertEq(hasInvalidatedTeleporting(A), false);

  // "y" is similar.
  C.y = 2;
  assertEq(hasInvalidatedTeleporting(receiver), false);
  assertEq(hasInvalidatedTeleporting(C), false);
  assertEq(hasInvalidatedTeleporting(B), true);
  assertEq(hasInvalidatedTeleporting(A), false);

  // "z" is only defined on A, so now A is affected.
  C.z = 3;
  assertEq(hasInvalidatedTeleporting(receiver), false);
  assertEq(hasInvalidatedTeleporting(C), false);
  assertEq(hasInvalidatedTeleporting(B), true);
  assertEq(hasInvalidatedTeleporting(A), true);
}
testShadowingPropStopsAtFirst();

// Ensure teleporting properties on Object.prototype is still possible.
assertEq(hasInvalidatedTeleporting(Object.prototype), false);