private-field-basics.js (4995B)
1 class A { 2 #x = 10 3 4 x() { 5 return this.#x; 6 } 7 ix() { 8 this.#x++; 9 } 10 static readx(o) { 11 return o.#x; 12 } 13 static optionalx(o) { 14 return o?.#x; 15 } 16 17 static orEqual(o, v) { 18 o.#x ||= v; 19 return o.#x; 20 } 21 22 setX(v) { 23 this.#x = v; 24 } 25 26 compoundInc() { 27 this.#x += 1; 28 return this.#x; 29 } 30 31 compoundDec() { 32 this.#x -= 1; 33 return this.#x; 34 } 35 36 #y = () => 'hi'; 37 invoke() { 38 return this.#y(); 39 } 40 41 static #z = 'static'; 42 gz() { 43 return A.#z; 44 } 45 46 sz(o) { 47 A.#z = o; 48 } 49 50 static sgz() { 51 return this.#z; 52 } 53 54 static ssz(o) { 55 this.#z = o; 56 } 57 58 static six(o) { 59 o.#x++; 60 } 61 62 static dix(o) { 63 o.#x--; 64 } 65 }; 66 67 for (var i = 0; i < 1000; i++) { 68 var a = new A(); 69 assertEq(a.x(), 10); 70 a.ix(); 71 assertEq(a.x(), 11); 72 assertEq(A.readx(a), 11); 73 assertEq(a.compoundInc(), 12); 74 assertEq(A.orEqual(a, 13), 12); 75 a.setX(null); 76 assertEq(A.orEqual(a, 12), 12); 77 assertEq(a.compoundDec(), 11); 78 assertEq(a.invoke(), 'hi'); 79 assertEq(a.gz(), 'static'); 80 assertEq(A.sgz(), 'static'); 81 A.ssz(i); 82 assertEq(A.sgz(), i); 83 a.sz(i + 1); 84 assertEq(A.sgz(), i + 1); 85 A.ssz('static'); // reset for next iteration! 86 87 assertEq(A.optionalx(a), 11); 88 assertEq(A.optionalx(null), undefined); 89 try { 90 A.optionalx({}); // Should throw type error 91 assertEq(0, 1); 92 } catch (TypeError) { 93 } 94 } 95 96 function assertThrows(fun, errorType) { 97 try { 98 fun(); 99 throw 'Expected error, but none was thrown'; 100 } catch (e) { 101 if (!(e instanceof errorType)) { 102 throw 'Wrong error type thrown'; 103 } 104 } 105 } 106 107 function testTypeErrors(v) { 108 assertThrows(() => A.readx(v), TypeError); // Read value 109 assertThrows(() => A.six(v), TypeError); // increment 110 assertThrows(() => A.dix(v), TypeError); // decrement 111 } 112 113 testTypeErrors(undefined); // Undefined 114 testTypeErrors({}); // Random object 115 testTypeErrors(1); // Random primitive 116 117 assertThrows( 118 () => eval('class B extends class { #x; } { g() { return super.#x; } }'), 119 SyntaxError); // Access super.#private 120 assertThrows( 121 () => eval('class C { #x = 10; static #x = 14; }'), 122 SyntaxError); // Duplicate name declaration. 123 assertThrows( 124 () => eval('delete this.#x'), 125 SyntaxError); // deleting a private field in non-strict mode. 126 127 class B extends class { 128 constructor(o) { 129 return o; 130 } 131 } 132 { 133 #x = 12; 134 static gx(o) { 135 return o.#x; 136 } 137 static sx(o) { 138 o.#x++; 139 } 140 } 141 142 var bn = new B(1); 143 var bu = new B(undefined); 144 145 // Test we can read an outer classes private fields. 146 class Outer { 147 #outer = 3; 148 test() { 149 let outerThis = this; 150 class Inner { 151 #inner = 2; 152 test() { 153 return outerThis.#outer; 154 } 155 } 156 return new Inner().test(); 157 } 158 } 159 160 var o = new Outer; 161 assertEq(o.test(), 3); 162 163 // IC tests: 164 165 var alreadyConstructedB = new B(); 166 assertEq(B.gx(alreadyConstructedB), 12); 167 168 function initIC(o) { 169 new B(o); 170 } 171 var array = []; 172 // warm up init IC 173 for (var i = 1; i < 1000; i++) { 174 var newB = {}; 175 initIC(newB); 176 } 177 178 // Successfully catch double initialization type error. 179 assertThrows(() => initIC(alreadyConstructedB), TypeError); 180 // Do it again, to make sure we didn't attach a stub that is invalid. 181 assertThrows(() => initIC(alreadyConstructedB), TypeError); 182 183 // Test getters work, and ICs can't be tricked. Setup an array of 184 // 185 // [B, B, B, B, ..., {}, {}] 186 // 187 // Then test that as we execute the sudden appearance of {} doesn't 188 // trick our ICs into setting or getting anything -- do it twice 189 // to make sure that we didn't get a stub that is invalid. 190 var elements = []; 191 for (var i = 0; i < 99; i++) { 192 elements.push(new B); 193 } 194 elements.push({}); 195 elements.push({}); 196 197 function getterCheck(e) { 198 assertEq(B.gx(e), 12); 199 } 200 201 function setterCheck(e) { 202 B.sx(e); 203 } 204 205 var checksPassed = 0; 206 try { 207 for (var e of elements) { 208 getterCheck(e); 209 checksPassed++; 210 } 211 throw `Shouldn't arrive here`; 212 } catch (e) { 213 if (!(e instanceof TypeError)) { 214 throw e; 215 } 216 // All but last element should have done the right thing. 217 assertEq(checksPassed, elements.length - 2); 218 } 219 220 checksPassed = 0; 221 try { 222 for (var e of elements) { 223 setterCheck(e); 224 checksPassed++; 225 } 226 throw `Shouldn't arrive here`; 227 } catch (e) { 228 if (!(e instanceof TypeError)) { 229 throw e; 230 } 231 // All but last element should have done the right thing. 232 assertEq(checksPassed, elements.length - 2); 233 } 234 235 // Verify setter did the thing, but throws in the correct places 236 for (var index in elements) { 237 if (index < elements.length - 2) { 238 assertEq(B.gx(elements[index]), 13); 239 } else { 240 assertThrows(() => { 241 B.gx(elements[index]); 242 }, TypeError); 243 } 244 } 245 246 // Megamorphic Cache Testing: 247 for (var i = 0; i < 100; i++) { 248 var inputs = [{ a: 1 }, { b: 2 }, { c: 3 }, { d: 4 }, { e: 5 }, new Proxy({}, {})]; 249 for (var o of inputs) { 250 assertThrows(() => B.gx(o), TypeError); 251 assertThrows(() => B.sx(o), TypeError); 252 new B(o); 253 assertEq(B.gx(o), 12); 254 B.sx(o); 255 assertEq(B.gx(o), 13); 256 } 257 }