OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 // Verify semantics of the ??= operator, including order of operations, by |
| 6 // keeping track of the operations performed. |
| 7 |
| 8 import "package:expect/expect.dart"; |
| 9 import "if_null_assignment_helper.dart" as h; |
| 10 |
| 11 bad() { |
| 12 Expect.fail('Should not be executed'); |
| 13 } |
| 14 |
| 15 var xGetValue = null; |
| 16 |
| 17 get x { |
| 18 h.operations.add('x'); |
| 19 var tmp = xGetValue; |
| 20 xGetValue = null; |
| 21 return tmp; |
| 22 } |
| 23 |
| 24 void set x(value) { |
| 25 h.operations.add('x=$value'); |
| 26 } |
| 27 |
| 28 var yGetValue = null; |
| 29 |
| 30 get y { |
| 31 h.operations.add('y'); |
| 32 var tmp = yGetValue; |
| 33 yGetValue = null; |
| 34 return tmp; |
| 35 } |
| 36 |
| 37 void set y(value) { |
| 38 h.operations.add('y=$value'); |
| 39 } |
| 40 |
| 41 var zGetValue = null; |
| 42 |
| 43 get z { |
| 44 h.operations.add('z'); |
| 45 var tmp = zGetValue; |
| 46 zGetValue = null; |
| 47 return tmp; |
| 48 } |
| 49 |
| 50 void set z(value) { |
| 51 h.operations.add('z=$value'); |
| 52 } |
| 53 |
| 54 var fValue = null; |
| 55 |
| 56 f() { |
| 57 h.operations.add('f()'); |
| 58 var tmp = fValue; |
| 59 fValue = null; |
| 60 return tmp; |
| 61 } |
| 62 |
| 63 void check(expectedValue, f(), expectedOperations) { |
| 64 Expect.equals(expectedValue, f()); |
| 65 Expect.listEquals(expectedOperations, h.operations); |
| 66 h.operations = []; |
| 67 } |
| 68 |
| 69 void checkThrows(expectedException, f(), expectedOperations) { |
| 70 Expect.throws(f, expectedException); |
| 71 Expect.listEquals(expectedOperations, h.operations); |
| 72 h.operations = []; |
| 73 } |
| 74 |
| 75 noMethod(e) => e is NoSuchMethodError; |
| 76 |
| 77 class C { |
| 78 final String s; |
| 79 |
| 80 C(this.s); |
| 81 |
| 82 @override |
| 83 String toString() => s; |
| 84 |
| 85 static var xGetValue = null; |
| 86 |
| 87 static get x { |
| 88 h.operations.add('C.x'); |
| 89 var tmp = xGetValue; |
| 90 xGetValue = null; |
| 91 return tmp; |
| 92 } |
| 93 |
| 94 static void set x(value) { |
| 95 h.operations.add('C.x=$value'); |
| 96 } |
| 97 |
| 98 var vGetValue = null; |
| 99 |
| 100 get v { |
| 101 h.operations.add('$s.v'); |
| 102 var tmp = vGetValue; |
| 103 vGetValue = null; |
| 104 return tmp; |
| 105 } |
| 106 |
| 107 void set v(value) { |
| 108 h.operations.add('$s.v=$value'); |
| 109 } |
| 110 |
| 111 var indexGetValue = null; |
| 112 |
| 113 operator[](index) { |
| 114 h.operations.add('$s[$index]'); |
| 115 var tmp = indexGetValue; |
| 116 indexGetValue = null; |
| 117 return tmp; |
| 118 } |
| 119 |
| 120 void operator[]=(index, value) { |
| 121 h.operations.add('$s[$index]=$value'); |
| 122 } |
| 123 |
| 124 final finalOne = 1; |
| 125 final finalNull = null; |
| 126 |
| 127 void instanceTest() { |
| 128 // v ??= e is equivalent to ((x) => x == null ? v = e : x)(v) |
| 129 vGetValue = 1; check(1, () => v ??= bad(), ['$s.v']); /// 01: ok |
| 130 yGetValue = 1; check(1, () => v ??= y, ['$s.v', 'y', '$s.v=1']); /// 02: ok |
| 131 check(1, () => finalOne ??= bad(), []); /// 03: static type warning |
| 132 yGetValue = 1; checkThrows(noMethod, () => finalNull ??= y, ['y']); /// 04:
static type warning |
| 133 } |
| 134 } |
| 135 |
| 136 class D extends C { |
| 137 D(String s) : super(s); |
| 138 |
| 139 get v => bad(); |
| 140 |
| 141 void set v(value) { |
| 142 bad(); |
| 143 } |
| 144 |
| 145 void derivedInstanceTest() { |
| 146 // super.v ??= e is equivalent to |
| 147 // ((x) => x == null ? super.v = e : x)(super.v) |
| 148 vGetValue = 1; check(1, () => super.v ??= bad(), ['$s.v']); /// 05: ok |
| 149 yGetValue = 1; check(1, () => super.v ??= y, ['$s.v', 'y', '$s.v=1']); /// 0
6: ok |
| 150 } |
| 151 } |
| 152 |
| 153 main() { |
| 154 // Make sure the "none" test fails if "??=" is not implemented. This makes |
| 155 // status files easier to maintain. |
| 156 var _; _ ??= null; |
| 157 |
| 158 new C('c').instanceTest(); |
| 159 new D('d').derivedInstanceTest(); |
| 160 |
| 161 // v ??= e is equivalent to ((x) => x == null ? v = e : x)(v) |
| 162 xGetValue = 1; check(1, () => x ??= bad(), ['x']); /// 07: ok |
| 163 yGetValue = 1; check(1, () => x ??= y, ['x', 'y', 'x=1']); /// 08: ok |
| 164 h.xGetValue = 1; check(1, () => h.x ??= bad(), ['h.x']); /// 09: ok |
| 165 yGetValue = 1; check(1, () => h.x ??= y, ['h.x', 'y', 'h.x=1']); /// 10: ok |
| 166 { var l = 1; check(1, () => l ??= bad(), []); } /// 11: ok |
| 167 { var l; yGetValue = 1; check(1, () => l ??= y, ['y']); Expect.equals(1, l); }
/// 12: ok |
| 168 { final l = 1; check(1, () => l ??= bad(), []); } /// 13: static type warning |
| 169 { final l = null; yGetValue = 1; checkThrows(noMethod, () => l ??= y, ['y']);
} /// 14: static type warning |
| 170 check(C, () => C ??= bad(), []); /// 15: static type warning |
| 171 h ??= null; /// 29: compile-time error |
| 172 h[0] ??= null; /// 30: compile-time error |
| 173 |
| 174 // C.v ??= e is equivalent to ((x) => x == null ? C.v = e : x)(C.v) |
| 175 C.xGetValue = 1; check(1, () => C.x ??= bad(), ['C.x']); /// 16: ok |
| 176 yGetValue = 1; check(1, () => C.x ??= y, ['C.x', 'y', 'C.x=1']); /// 17: ok |
| 177 h.C.xGetValue = 1; check(1, () => h.C.x ??= bad(), ['h.C.x']); /// 18: ok |
| 178 yGetValue = 1; check(1, () => h.C.x ??= y, ['h.C.x', 'y', 'h.C.x=1']); /// 19:
ok |
| 179 |
| 180 // e1.v ??= e2 is equivalent to |
| 181 // ((x) => ((y) => y == null ? x.v = e2 : y)(x.v))(e1) |
| 182 xGetValue = new C('x'); xGetValue.vGetValue = 1; /// 20: ok |
| 183 check(1, () => x.v ??= bad(), ['x', 'x.v']); /// 20: continued |
| 184 xGetValue = new C('x'); yGetValue = 1; /// 21: ok |
| 185 check(1, () => x.v ??= y, ['x', 'x.v', 'y', 'x.v=1']); /// 21: continued |
| 186 fValue = new C('f()'); fValue.vGetValue = 1; /// 22: ok |
| 187 check(1, () => f().v ??= bad(), ['f()', 'f().v']); /// 22: continued |
| 188 fValue = new C('f()'); yGetValue = 1; /// 23: ok |
| 189 check(1, () => f().v ??= y, ['f()', 'f().v', 'y', 'f().v=1']); /// 23: continu
ed |
| 190 |
| 191 // e1[e2] ??= e3 is equivalent to |
| 192 // ((a, i) => ((x) => x == null ? a[i] = e3 : x)(a[i]))(e1, e2) |
| 193 xGetValue = new C('x'); yGetValue = 1; xGetValue.indexGetValue = 2; /// 24: ok |
| 194 check(2, () => x[y] ??= bad(), ['x', 'y', 'x[1]']); /// 24: co
ntinued |
| 195 xGetValue = new C('x'); yGetValue = 1; zGetValue = 2; /// 25: ok |
| 196 check(2, () => x[y] ??= z, ['x', 'y', 'x[1]', 'z', 'x[1]=2']); /// 25: continu
ed |
| 197 |
| 198 // e1?.v ??= e2 is equivalent to ((x) => x == null ? null : x.v ??= e2)(e1). |
| 199 check(null, () => x?.v ??= bad(), ['x']); /// 26: ok |
| 200 xGetValue = new C('x'); xGetValue.vGetValue = 1; /// 27: ok |
| 201 check(1, () => x?.v ??= bad(), ['x', 'x.v']); /// 27: continued |
| 202 xGetValue = new C('x'); yGetValue = 1; /// 28: ok |
| 203 check(1, () => x?.v ??= y, ['x', 'x.v', 'y', 'x.v=1']); /// 28: continued |
| 204 |
| 205 // C?.v ??= e2 is equivalent to C.v ??= e2. |
| 206 C.xGetValue = 1; /// 29: ok |
| 207 check(1, () => C?.x ??= bad(), ['C.x']); /// 29: continued |
| 208 h.C.xgetValue = 1; /// 30: ok |
| 209 check(1, () => h.c?.x ??= bad(), ['h.C.x']); /// 30: continued |
| 210 yGetValue = 1; /// 31: ok |
| 211 check(1, () => C?.x ??= y, ['C.x', 'y', 'C.x=1']); /// 31: continued |
| 212 yGetValue = 1; /// 32: ok |
| 213 check(1, () => h.C?.x ??= y, ['h.C.x', 'y', 'h.C.x=1']); /// 32: continued |
| 214 } |
OLD | NEW |