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 // SharedOptions=-Dsend_stats=true |
| 5 |
| 6 /// Tests that we compute send metrics correctly in many simple scenarios. |
| 7 library dart2js.test.send_measurements_test; |
| 8 |
| 9 import 'dart:async'; |
| 10 import 'package:test/test.dart'; |
| 11 import 'package:dart2js_info/info.dart'; |
| 12 import 'memory_compiler.dart'; |
| 13 import 'dart:io'; |
| 14 |
| 15 main() { |
| 16 test('nothing is reachable, nothing to count', () { |
| 17 return _check(''' |
| 18 main() {} |
| 19 test() { int x = 3; } |
| 20 '''); |
| 21 }); |
| 22 |
| 23 test('local variable read', () { |
| 24 return _check(''' |
| 25 main() => test(); |
| 26 test() { int x = 3; int y = x; } |
| 27 ''', |
| 28 localSend: 1); // from `int y = x`; |
| 29 }); |
| 30 |
| 31 test('generative constructor call', () { |
| 32 return _check(''' |
| 33 class A { |
| 34 get f => 1; |
| 35 } |
| 36 main() => test(); |
| 37 test() { new A(); } |
| 38 ''', |
| 39 constructorSend: 1); // from new A() |
| 40 }); |
| 41 |
| 42 group('instance call', () { |
| 43 test('monomorphic only one implementor', () { |
| 44 return _check(''' |
| 45 class A { |
| 46 get f => 1; |
| 47 } |
| 48 main() => test(); |
| 49 test() { new A().f; } |
| 50 ''', |
| 51 constructorSend: 1, // new A() |
| 52 instanceSend: 1); // f resolved to A.f |
| 53 }); |
| 54 |
| 55 test('monomorphic only one type possible from types', () { |
| 56 return _check(''' |
| 57 class A { |
| 58 get f => 1; |
| 59 } |
| 60 class B extends A { |
| 61 get f => 1; |
| 62 } |
| 63 main() => test(); |
| 64 test() { new B().f; } |
| 65 ''', |
| 66 constructorSend: 1, |
| 67 instanceSend: 1); // f resolved to B.f |
| 68 }); |
| 69 |
| 70 test('monomorphic only one type possible from liveness', () { |
| 71 return _check(''' |
| 72 class A { |
| 73 get f => 1; |
| 74 } |
| 75 class B extends A { |
| 76 get f => 1; |
| 77 } |
| 78 main() => test(); |
| 79 test() { A x = new B(); x.f; } |
| 80 ''', |
| 81 constructorSend: 1, // new B() |
| 82 localSend: 1, // x in x.f |
| 83 instanceSend: 1); // x.f known to resolve to B.f |
| 84 }); |
| 85 |
| 86 test('monomorphic one possible, more than one live', () { |
| 87 return _check(''' |
| 88 class A { |
| 89 get f => 1; |
| 90 } |
| 91 class B extends A { |
| 92 get f => 1; |
| 93 } |
| 94 main() { new A(); test(); } |
| 95 test() { B x = new B(); x.f; } |
| 96 ''', |
| 97 constructorSend: 1, // new B() |
| 98 localSend: 1, // x in x.f |
| 99 instanceSend: 1); // x.f resolves to B.f |
| 100 }); |
| 101 |
| 102 test('polymorphic-virtual couple possible types from liveness', () { |
| 103 // Note: this would be an instanceSend if we used the inferrer. |
| 104 return _check(''' |
| 105 class A { |
| 106 get f => 1; |
| 107 } |
| 108 class B extends A { |
| 109 get f => 1; |
| 110 } |
| 111 main() { new A(); test(); } |
| 112 test() { A x = new B(); x.f; } |
| 113 ''', |
| 114 constructorSend: 1, // new B() |
| 115 localSend: 1, // x in x.f |
| 116 virtualSend: 1); // x.f may be A.f or B.f (types alone is not enough) |
| 117 }); |
| 118 |
| 119 test("polymorphic-dynamic: type annotations don't help", () { |
| 120 return _check(''' |
| 121 class A { |
| 122 get f => 1; |
| 123 } |
| 124 class B extends A { |
| 125 get f => 1; |
| 126 } |
| 127 main() { new A(); test(); } |
| 128 test() { var x = new B(); x.f; } |
| 129 ''', |
| 130 constructorSend: 1, // new B() |
| 131 localSend: 1, // x in x.f |
| 132 dynamicSend: 1); // x.f could be any `f` or no `f` |
| 133 }); |
| 134 }); |
| 135 |
| 136 group('instance this call', () { |
| 137 test('monomorphic only one implementor', () { |
| 138 return _check(''' |
| 139 class A { |
| 140 get f => 1; |
| 141 test() => this.f; |
| 142 } |
| 143 main() => new A().test(); |
| 144 ''', |
| 145 instanceSend: 1); // this.f resolved to A.f |
| 146 }); |
| 147 |
| 148 test('monomorphic only one type possible from types & liveness', () { |
| 149 return _check(''' |
| 150 class A { |
| 151 get f => 1; |
| 152 test() => this.f; |
| 153 } |
| 154 class B extends A { |
| 155 get f => 1; |
| 156 } |
| 157 main() => new B().test(); |
| 158 ''', |
| 159 instanceSend: 1); // this.f resolved to B.f |
| 160 }); |
| 161 |
| 162 test('polymorphic-virtual couple possible types from liveness', () { |
| 163 // Note: this would be an instanceSend if we used the inferrer. |
| 164 return _check(''' |
| 165 class A { |
| 166 get f => 1; |
| 167 test() => this.f; |
| 168 } |
| 169 class B extends A { |
| 170 get f => 1; |
| 171 } |
| 172 main() { new A(); new B().test(); } |
| 173 ''', |
| 174 virtualSend: 1); // this.f may be A.f or B.f |
| 175 }); |
| 176 }); |
| 177 |
| 178 group('noSuchMethod', () { |
| 179 test('error will be thrown', () { |
| 180 return _check(''' |
| 181 class A { |
| 182 } |
| 183 main() { test(); } |
| 184 test() { new A().f; } |
| 185 ''', |
| 186 constructorSend: 1, // new B() |
| 187 nsmErrorSend: 1); // f not there, A has no nSM |
| 188 }); |
| 189 |
| 190 test('nSM will be called - one option', () { |
| 191 return _check(''' |
| 192 class A { |
| 193 noSuchMethod(i) => null; |
| 194 } |
| 195 main() { test(); } |
| 196 test() { new A().f; } |
| 197 ''', |
| 198 constructorSend: 1, // new B() |
| 199 singleNsmCallSend: 1); // f not there, A has nSM |
| 200 }); |
| 201 |
| 202 // TODO(sigmund): is it worth splitting multiNSMvirtual? |
| 203 test('nSM will be called - multiple options', () { |
| 204 return _check(''' |
| 205 class A { |
| 206 noSuchMethod(i) => null; |
| 207 } |
| 208 class B extends A { |
| 209 noSuchMethod(i) => null; |
| 210 } |
| 211 main() { new A(); test(); } |
| 212 test() { A x = new B(); x.f; } |
| 213 ''', |
| 214 constructorSend: 1, // new B() |
| 215 localSend: 1, // x in x.f |
| 216 multiNsmCallSend: 1); // f not there, A has nSM |
| 217 }); |
| 218 |
| 219 // TODO(sigmund): is it worth splitting multiNSMvirtual? |
| 220 test('nSM will be called - multiple options', () { |
| 221 return _check(''' |
| 222 class A { |
| 223 noSuchMethod(i) => null; |
| 224 } |
| 225 class B extends A { |
| 226 // don't count A's nsm as distinct |
| 227 } |
| 228 main() { new A(); test(); } |
| 229 test() { A x = new B(); x.f; } |
| 230 ''', |
| 231 constructorSend: 1, // new B() |
| 232 localSend: 1, // x in x.f |
| 233 singleNsmCallSend: 1); // f not there, A has nSM |
| 234 }); |
| 235 |
| 236 test('nSM will be called - multiple options', () { |
| 237 return _check(''' |
| 238 class A { |
| 239 noSuchMethod(i) => null; |
| 240 } |
| 241 class B extends A { |
| 242 get f => null; |
| 243 } |
| 244 main() { new A(); test(); } |
| 245 test() { A x = new B(); x.f; } |
| 246 ''', |
| 247 constructorSend: 1, // new B() |
| 248 localSend: 1, // x in x.f |
| 249 dynamicSend: 1); // f not known to be there there, A has nSM |
| 250 }); |
| 251 |
| 252 test('nSM in super', () { |
| 253 return _check(''' |
| 254 class A { |
| 255 noSuchMethod(i) => null; |
| 256 } |
| 257 class B extends A { |
| 258 get f => super.f; |
| 259 } |
| 260 main() { new A(); test(); } |
| 261 test() { A x = new B(); x.f; } |
| 262 ''', |
| 263 singleNsmCallSend: 1, // super.f |
| 264 testMethod: 'f'); |
| 265 }); |
| 266 }); |
| 267 } |
| 268 |
| 269 |
| 270 /// Checks that the `test` function in [code] produces the given distribution of |
| 271 /// sends. |
| 272 _check(String code, {int staticSend: 0, int superSend: 0, int localSend: 0, |
| 273 int constructorSend: 0, int typeVariableSend: 0, int nsmErrorSend: 0, |
| 274 int singleNsmCallSend: 0, int instanceSend: 0, int interceptorSend: 0, |
| 275 int multiNsmCallSend: 0, int virtualSend: 0, int multiInterceptorSend: 0, |
| 276 int dynamicSend: 0, String testMethod: 'test'}) async { |
| 277 |
| 278 // Set up the expectation. |
| 279 var expected = new Measurements(); |
| 280 int monomorphic = staticSend + superSend + localSend + constructorSend + |
| 281 typeVariableSend + nsmErrorSend + singleNsmCallSend + instanceSend + |
| 282 interceptorSend; |
| 283 int polymorphic = multiNsmCallSend + virtualSend + multiInterceptorSend + |
| 284 dynamicSend; |
| 285 |
| 286 expected.counters[Metric.monomorphicSend] = monomorphic; |
| 287 expected.counters[Metric.staticSend] = staticSend; |
| 288 expected.counters[Metric.superSend] = superSend; |
| 289 expected.counters[Metric.localSend] = localSend; |
| 290 expected.counters[Metric.constructorSend] = constructorSend; |
| 291 expected.counters[Metric.typeVariableSend] = typeVariableSend; |
| 292 expected.counters[Metric.nsmErrorSend] = nsmErrorSend; |
| 293 expected.counters[Metric.singleNsmCallSend] = singleNsmCallSend; |
| 294 expected.counters[Metric.instanceSend] = instanceSend; |
| 295 expected.counters[Metric.interceptorSend] = interceptorSend; |
| 296 |
| 297 expected.counters[Metric.polymorphicSend] = polymorphic; |
| 298 expected.counters[Metric.multiNsmCallSend] = multiNsmCallSend; |
| 299 expected.counters[Metric.virtualSend] = virtualSend; |
| 300 expected.counters[Metric.multiInterceptorSend] = multiInterceptorSend; |
| 301 expected.counters[Metric.dynamicSend] = dynamicSend; |
| 302 |
| 303 expected.counters[Metric.send] = monomorphic + polymorphic; |
| 304 |
| 305 // Run the compiler to get the results. |
| 306 var all = await _compileAndGetStats(code); |
| 307 var function = all.functions.firstWhere((f) => f.name == testMethod, |
| 308 orElse: () => null); |
| 309 var result = function?.measurements; |
| 310 if (function == null) { |
| 311 expect(expected.counters[Metric.send], 0); |
| 312 return; |
| 313 } |
| 314 |
| 315 expect(result, isNotNull); |
| 316 |
| 317 _compareMetric(Metric key) { |
| 318 var expectedValue = expected.counters[key]; |
| 319 var value = result.counters[key]; |
| 320 if (value == null) value = 0; |
| 321 if (value == expectedValue) return; |
| 322 expect(expectedValue, value, |
| 323 reason: "count for `$key` didn't match:\n" |
| 324 "expected measurements:\n${recursiveDiagnosticString(expected, key)}\n" |
| 325 "actual measurements:\n${recursiveDiagnosticString(result, key)}"); |
| 326 } |
| 327 |
| 328 _compareMetric(Metric.send); |
| 329 expected.counters.keys.forEach(_compareMetric); |
| 330 } |
| 331 |
| 332 /// Helper that runs the compiler and returns the [GlobalResult] computed for |
| 333 /// it. |
| 334 Future<AllInfo> _compileAndGetStats(String program) async { |
| 335 var result = await runCompiler( |
| 336 memorySourceFiles: {'main.dart': program}, options: ['--dump-info']); |
| 337 expect(result.compiler.compilationFailed, isFalse); |
| 338 return result.compiler.dumpInfoTask.infoCollector.result; |
| 339 } |
OLD | NEW |