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